私はアーキテクチャコンポーネントを使用してアプリケーションを作成しています。ViewModelProviders java.lang.RuntimeException AndroidViewModelを継承するクラスをインスタンス化しようとしています
デバッグビルドはうまく動作しますが、リリースAPKをテストすると、アプリケーションは以下のスタックトレースでクラッシュします。
奇妙なのは、同じ方法で設定されたビューモデルを持つ6つのフラグメントがあることです。 2人がクラッシュし、4人が意図したとおりに動作します。
プロジェクトは、プロガードを縮小していません。
私は、次の依存関係を持つアーキテクチャコンポーネントを使用しています:アーキテクチャコンポーネントの
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')にしようとすると、同じ問題が発生します。
私はこのようなフラグメントからの私のviewmodelをインスタンス化:
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)
いくつかのより多くのコード:
@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); } } }
はのviewmodelをインスタンス化するために、ライブラリのコードを実行すると、以下の例外を除いて、アプリがクラッシュします:
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が 'Application'値を取る場所を知らないので、この方法でViewModelを初期化することができないと思います。試してみてください、それが問題なら私はより詳細な解決策を投稿します。 – MatPag