2017-10-20 48 views
0

我正在使用體系結構組件創建應用程序。ViewModelProviders java.lang.RuntimeException試圖實例化繼承AndroidViewModel的類

調試版本運行良好,但是當測試版本APK時,應用程序崩潰,下面會看到堆棧跟蹤。

奇怪的是,我有6個視圖模型的片段設置相同的方式。其中2人墜毀,其中4人按預期工作。

該項目沒有做任何proguard縮小。

我使用體系結構組件具有下列相關性:

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

架構組件的版本是 '1.0.0-β2'(拱= '1.0.0-β2')。如果我嘗試版本的體系結構組件是'1.0.0-rc1'(arch ='1.0.0-rc1'),我會遇到同樣的問題。

我實例從片段我的視圖模型是這樣的:

@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); 
     } 
    } 
} 

當運行庫代碼實例化視圖模型的應用程序崩潰,但以下情況除外:

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)  

更多的代碼:

該viewmodel:

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); 
    } 
} 

而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

您應該發佈'OtherWorkViewModel'代碼來幫助 – MatPag

+0

@MatPag完成! :-) –

+0

嘗試刪除'OtherWorkViewModel'構造函數中的'Application'參數(因此最終得到一個默認的空構造函數並註釋掉numberFormat)。我懷疑Factory不能以這種方式初始化ViewModel,因爲它不知道應該在哪裏獲取Application值。試着讓我知道它是否有效,如果這是問題,我會發布更詳細的解決方案。 – MatPag

回答

2

讓您OtherWorkViewModel構造public,看看有沒有什麼幫助。

+0

萬歲!這有幫助! :-) 非常感謝! –