2017-10-20 9 views
0

Je crée une application en utilisant des composants d'architecture.ViewModelProviders java.lang.RuntimeException essayant d'instancier une classe qui hérite AndroidViewModel

La version de débogage fonctionne correctement, mais lors du test de la version APK, l'application se bloque avec la trace de la pile vue ci-dessous. Ce qui est étrange, c'est que j'ai 6 fragments avec viewmodels qui sont configurés de la même manière. 2 d'entre eux s'écrasent, 4 d'entre eux fonctionnent comme prévu.

Le projet ne fait aucune minoration proguard.

J'utilise des composants d'architecture avec les dépendances suivantes:

compile "android.arch.lifecycle:runtime:${arch}" 
compile "android.arch.lifecycle:extensions:${arch}" 
annotationProcessor "android.arch.lifecycle:compiler:${arch}" 

version des composants d'architecture est 'beta2-1.0.0' (arch = 'beta2-1.0.0). Si j'essaie la version des composants de l'architecture est '1.0.0-rc1' (arch = '1.0.0-rc1') je reçois le même problème.

J'instancier mon viewmodel du fragment comme celui-ci:

@Override 
public void onCreate(@Nullable Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 

    otherWorkViewModel = ViewModelProviders.of(this).get(OtherWorkViewModel.class); 

    if (savedInstanceState == null) { 

     // Check for arguments to fragment (section object and position in list) 
     if (getArguments() != null) { 
      OtherWork otherWork = (OtherWork) getArguments().get("otherWork"); 
      otherWorkViewModel.setOtherWork(otherWork); 

      int position = getArguments().getInt("position"); 
      otherWorkViewModel.setPosition(position); 
     } 
    } 
} 

Lors de l'exécution du code de la bibliothèque pour instancier le viewmodel les application se bloque à l'exception suivante:

10-20 08:41:03.555 2102-2102/no.example.thisapp E/AndroidRuntime: FATAL EXCEPTION: main 
    Process: no.example.thisapp, PID: 2102 
    java.lang.RuntimeException: Cannot create an instance of class no.example.thisapp.ui.job.pavingreport.OtherWorkViewModel 
     at android.arch.lifecycle.ViewModelProviders$DefaultFactory.create(ViewModelProviders.java:149) 
     at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:128) 
     at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:96) 
     at no.example.thisapp.ui.job.pavingreport.PavingReportDialogOtherWork.onCreate(PavingReportDialogOtherWork.java:57) 
     at android.support.v4.app.Fragment.performCreate(Fragment.java:2339) 
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1377) 
     at android.support.v4.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1109) 
     at android.support.v4.app.FragmentTransition.calculateFragments(FragmentTransition.java:996) 
     at android.support.v4.app.FragmentTransition.startTransitions(FragmentTransition.java:99) 
     at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2364) 
     at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2322) 
     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2229) 
     at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:700) 
     at android.os.Handler.handleCallback(Handler.java:751) 
     at android.os.Handler.dispatchMessage(Handler.java:95) 
     at android.os.Looper.loop(Looper.java:154) 
     at android.app.ActivityThread.main(ActivityThread.java:6077) 
     at java.lang.reflect.Method.invoke(Native Method) 
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866) 
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756) 
     Caused by: java.lang.NoSuchMethodException: <init> [class android.app.Application] 
     at java.lang.Class.getConstructor0(Class.java:2204) 
     at java.lang.Class.getConstructor(Class.java:1683) 
     at android.arch.lifecycle.ViewModelProviders$DefaultFactory.create(ViewModelProviders.java:147) 
     at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:128)  
     at android.arch.lifecycle.ViewModelProvider.get(ViewModelProvider.java:96)  
     at no.example.thisapp.ui.job.pavingreport.PavingReportDialogOtherWork.onCreate(PavingReportDialogOtherWork.java:57)  
     at android.support.v4.app.Fragment.performCreate(Fragment.java:2339)  
     at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1377)  
     at android.support.v4.app.FragmentTransition.addToFirstInLastOut(FragmentTransition.java:1109)  
     at android.support.v4.app.FragmentTransition.calculateFragments(FragmentTransition.java:996)  
     at android.support.v4.app.FragmentTransition.startTransitions(FragmentTransition.java:99)  
     at android.support.v4.app.FragmentManagerImpl.executeOpsTogether(FragmentManager.java:2364)  
     at android.support.v4.app.FragmentManagerImpl.removeRedundantOperationsAndExecute(FragmentManager.java:2322)  
     at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:2229)  
     at android.support.v4.app.FragmentManagerImpl$1.run(FragmentManager.java:700)  
     at android.os.Handler.handleCallback(Handler.java:751)  
     at android.os.Handler.dispatchMessage(Handler.java:95)  
     at android.os.Looper.loop(Looper.java:154)  
     at android.app.ActivityThread.main(ActivityThread.java:6077)  
     at java.lang.reflect.Method.invoke(Native Method)  
     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:866)  
     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:756)  

code plus: Le mode de vue:

package no.example.thisapp.ui.job.pavingreport; 

import android.app.Application; 
import android.databinding.Bindable; 
import android.util.Log; 

import com.android.databinding.library.baseAdapters.BR; 

import java.text.NumberFormat; 
import java.text.ParseException; 

import no.inforte.android.databinding.BaseObservableViewModel; 
import no.inforte.android.validation.Misc; 
import no.example.thisapp.R; 
import no.example.thisapp.model.pavingreport.OtherWork; 


public final class OtherWorkViewModel extends BaseObservableViewModel { 

    @SuppressWarnings("unused") 
    private static final String TAG = "OtherWorkViewModel"; 

    private OtherWork otherWork; 
    private int position; 

    private NumberFormat numberFormat; 

    // Shadow fields for otherArea model object 

    private String areaGluedString; 
    private String handPavedTonString; 
    private String nightWorkTonString; 




    OtherWorkViewModel(Application application) { 
     super(application); 

     numberFormat = Misc.getNumberFormat(application); 
    } 




    // Used when: 
    // - accessing values from layout 
    // - Returning values to PavingReportListFragment 
    public OtherWork getOtherWork() { 
     return otherWork; 
    } 




    // Used when: 
    // - Returning values to PavingReportListFragment 
    public int getPosition() { 
     return position; 
    } 




    public void setOtherWork(OtherWork otherWork) { 
     this.otherWork = otherWork; 
     this.areaGluedString = (otherWork.getAreaGlued() != null && !otherWork.getAreaGlued().isNaN()) ? numberFormat.format(otherWork.getAreaGlued()) : ""; 
     this.handPavedTonString = (otherWork.getHandPavedTon() != null && !otherWork.getHandPavedTon().isNaN()) ? numberFormat.format(otherWork.getHandPavedTon()) : ""; 
     this.nightWorkTonString = (otherWork.getHandPavedTon() != null && !otherWork.getHandPavedTon().isNaN()) ? numberFormat.format(otherWork.getHandPavedTon()) : ""; 
    } 




    public void setPosition(int position) { 
     this.position = position; 
    } 




    public boolean isValidInput() { 
     boolean tmpIsValid = true; 

     if (!validDrains()) tmpIsValid = false; 
     if (!validManHoles()) tmpIsValid = false; 
     if (!validOther()) tmpIsValid = false; 
     if (!validHandPavedTon()) tmpIsValid = false; 
     if (!validNightWorkTon()) tmpIsValid = false; 
     if (!validAreaGlued()) tmpIsValid = false; 

     return tmpIsValid; 
    } 


    // Shadow for otherWork.areaGlued (Double) 




    @Bindable 
    public String getAreaGluedString() { 
     return areaGluedString; 
    } 




    @Bindable 
    public String getAreaGluedStringError() { 
     return Misc.validateDecimalNumberString(areaGluedString, this.getApplication()); 
    } 




    public void setAreaGluedString(String areaGluedString) { 
     this.areaGluedString = areaGluedString; 

     if (Misc.isValidDecimalNumber(this.areaGluedString, this.getApplication())) { 
      try { 
       Number number = numberFormat.parse(this.areaGluedString); 
       otherWork.setAreaGlued(number.doubleValue()); 
      } catch (ParseException e) { 
       e.printStackTrace(); 
      } 
     } 

     notifyPropertyChanged(BR.areaGluedString); 
     notifyPropertyChanged(BR.areaGluedStringError); 
    } 




    private boolean validAreaGlued() { 
     return (otherWork.getAreaGlued() != null && !otherWork.getAreaGlued().isNaN()); 
    } 


    // Shadow for otherWork.handPavedTon (Double) 




    @Bindable 
    public String getHandPavedTonString() { 
     return handPavedTonString; 
    } 




    @Bindable 
    public String getHandPavedTonStringError() { 
     return Misc.validateDecimalNumberString(handPavedTonString, this.getApplication()); 
    } 




    public void setHandPavedTonString(String handPavedTonString) { 
     this.handPavedTonString = handPavedTonString; 

     if (Misc.isValidDecimalNumber(this.handPavedTonString, this.getApplication())) { 
      try { 
       Number number = numberFormat.parse(this.handPavedTonString); 
       otherWork.setHandPavedTon(number.doubleValue()); 
      } catch (ParseException e) { 
       e.printStackTrace(); 
      } 
     } 

     notifyPropertyChanged(BR.handPavedTonString); 
     notifyPropertyChanged(BR.handPavedTonStringError); 
    } 




    private boolean validHandPavedTon() { 
     return (otherWork.getHandPavedTon() != null && !otherWork.getHandPavedTon().isNaN()); 
    } 


    // Shadow for otherWork.nightWorkTon (Double) 




    @Bindable 
    public String getNightWorkTonString() { 
     return nightWorkTonString; 
    } 




    @Bindable 
    public String getNightWorkTonStringError() { 
     return Misc.validateDecimalNumberString(nightWorkTonString, getApplication()); 
    } 




    public void setNightWorkTonString(String nightWorkTonString) { 
     this.nightWorkTonString = nightWorkTonString; 

     if (Misc.isValidDecimalNumber(this.nightWorkTonString, this.getApplication())) { 
      try { 
       Number number = numberFormat.parse(this.nightWorkTonString); 
       otherWork.setNightWorkTon(number.doubleValue()); 
      } catch (ParseException e) { 
       e.printStackTrace(); 
      } 
     } 

     notifyPropertyChanged(BR.nightWorkTonString); 
     notifyPropertyChanged(BR.nightWorkTonStringError); 
    } 




    private boolean validNightWorkTon() { 
     return (otherWork.getNightWorkTon() != null && !otherWork.getNightWorkTon().isNaN()); 
    } 




    @Bindable 
    public String getDrainsString() { 
     if (otherWork.getDrains() != null && otherWork.getDrains() == (int) otherWork.getDrains()) { 
      return otherWork.getDrains().toString(); 
     } else return ""; 
    } 




    private boolean validDrains() { 
     return (otherWork.getDrains() != null && otherWork.getDrains() == (int) otherWork.getDrains()); 
    } 




    @Bindable 
    public String getDrainsStringError() { 
     if (validDrains()) 
      return null; 
     return getApplication().getString(R.string.error_value_required); 
    } 




    public void setDrainsString(String drainsString) { 
     try { 
      int drains = Integer.parseInt(drainsString); 
      otherWork.setDrains(drains); 
      notifyPropertyChanged(BR.drainsStringError); 
     } catch (NumberFormatException ex) { 
      Log.d(TAG, "Could not parse integer value..."); 
     } 
    } 




    @Bindable 
    public String getManHolesString() { 
     if (otherWork.getManHoles() != null && otherWork.getManHoles() == (int) otherWork.getManHoles()) { 
      return otherWork.getManHoles().toString(); 
     } else return ""; 
    } 




    private boolean validManHoles() { 
     return (otherWork.getManHoles() != null && otherWork.getManHoles() == (int) otherWork.getManHoles()); 
    } 




    @Bindable 
    public String getManHolesStringError() { 
     if (validManHoles()) return null; 
     return getApplication().getString(R.string.error_value_required); 
    } 




    public void setManHolesString(String manHolesString) { 
     try { 
      int manHoles = Integer.parseInt(manHolesString); 
      otherWork.setManHoles(manHoles); 
      notifyPropertyChanged(BR.manHolesStringError); 
     } catch (NumberFormatException ex) { 
      Log.d(TAG, "Could not parse integer value..."); 
     } 
    } 




    private boolean validOther() { 
     return (otherWork.getOther() != null && !otherWork.getOther().isEmpty()); 
    } 




    @Bindable 
    public String getOtherError() { 
     return (validOther()) ? null : getApplication().getString(R.string.error_value_required); 
    } 
} 

Et le BaseObservableViewModel:

package no.inforte.android.databinding; 


import android.app.Application; 
import android.arch.lifecycle.AndroidViewModel; 
import android.databinding.Bindable; 
import android.databinding.Observable; 
import android.databinding.PropertyChangeRegistry; 

public class BaseObservableViewModel extends AndroidViewModel implements Observable { 
    private transient PropertyChangeRegistry mCallbacks; 

    public BaseObservableViewModel(Application application) { 
     super(application); 
    } 



    // Code below is copied from com.android.databinding.BaseObservable so we can use this ViewModel as our backing Databinding model 
    // Solution came from twitter tip from Yigit Boyar and this article: 
    // https://willowtreeapps.com/ideas/google-i-o-2017-the-viewmodel-is-nice-from-up-here 

    @Override 
    public void addOnPropertyChangedCallback(Observable.OnPropertyChangedCallback callback) { 
     synchronized (this) { 
      if (mCallbacks == null) { 
       mCallbacks = new PropertyChangeRegistry(); 
      } 
     } 
     mCallbacks.add(callback); 
    } 

    @Override 
    public void removeOnPropertyChangedCallback(Observable.OnPropertyChangedCallback callback) { 
     synchronized (this) { 
      if (mCallbacks == null) { 
       return; 
      } 
     } 
     mCallbacks.remove(callback); 
    } 

    /** 
    * Notifies listeners that all properties of this instance have changed. 
    */ 
    public void notifyChange() { 
     synchronized (this) { 
      if (mCallbacks == null) { 
       return; 
      } 
     } 
     mCallbacks.notifyCallbacks(this, 0, null); 
    } 

    /** 
    * Notifies listeners that a specific property has changed. The getter for the property 
    * that changes should be marked with {@link Bindable} to generate a field in 
    * <code>BR</code> to be used as <code>fieldId</code>. 
    * 
    * @param fieldId The generated BR id for the Bindable field. 
    */ 
    public void notifyPropertyChanged(int fieldId) { 
     synchronized (this) { 
      if (mCallbacks == null) { 
       return; 
      } 
     } 
     mCallbacks.notifyCallbacks(this, fieldId, null); 
    } 

} 
+0

Vous devriez publier le code 'OtherWorkViewModel' pour aider – MatPag

+0

@MatPag Done! :-) –

+0

Essayant de supprimer le paramètre 'Application' dans le constructeur' OtherWorkViewModel' (pour finir avec un constructeur vide par défaut et commenter numberFormat). Je suppose que l'Usine n'est pas capable d'initier un ViewModel de cette manière car elle ne sait pas où prendre la valeur 'Application'. Essayez et laissez-moi savoir si cela fonctionne, si c'est le problème, je vais poster une solution plus détaillée. – MatPag

Répondre

2

Faites votre OtherWorkViewModel constructeur public et voir si cela aide.

+0

Hourra! Cela a aidé! :-) Merci beaucoup! –