我正在使用體系結構組件創建應用程序。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);
}
}
您應該發佈'OtherWorkViewModel'代碼來幫助 – MatPag
@MatPag完成! :-) –
嘗試刪除'OtherWorkViewModel'構造函數中的'Application'參數(因此最終得到一個默認的空構造函數並註釋掉numberFormat)。我懷疑Factory不能以這種方式初始化ViewModel,因爲它不知道應該在哪裏獲取Application值。試着讓我知道它是否有效,如果這是問題,我會發布更詳細的解決方案。 – MatPag