Java.lang.RuntimeException:
Unable to start activity ComponentInfo{ir.orangehat.movieinfo/ir.orangehat.movieinfo.application.home.HomeActivity}: Java.lang.RuntimeException:
Cannot create an instance of class ir.orangehat.movieinfo.application.home.HomeViewModel
public class HomeViewModel extends AndroidViewModel {
private MovieRepository movieRepository;
public HomeViewModel(@NonNull Application application, Context context, LifecycleOwner lifecycleOwner) {
super(application);
movieRepository = new MovieRepository(lifecycleOwner, context);
}
LiveData<List<Movie>> getMovies() {
return movieRepository.getMovies();
}}
public class MovieRepository extends BaseRepository {
private LifecycleOwner lifecycleOwner;
private MovieApi movieApi;
private MovieDatabaseHelper movieDatabaseHelper;
public MovieRepository(LifecycleOwner lifecycleOwner, Context context) {
this.lifecycleOwner = lifecycleOwner;
movieApi = getRetrofitHelper().getService(MovieApi.class);
movieDatabaseHelper = new MovieDatabaseHelper(context);
}
public LiveData<List<Movie>> getMovies() {
LiveData<List<Movie>> moviesLiveData = movieApi.getMovieList();
moviesLiveData.observe(lifecycleOwner, new Observer<List<Movie>>() {
@Override
public void onChanged(@Nullable List<Movie> movieArrayList) {
movieDatabaseHelper.Save(movieArrayList);
}
});
return movieDatabaseHelper.getAll();
} }
public class HomeActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// the error is here
HomeViewModel homeViewModel = ViewModelProviders.of(this).get(HomeViewModel.class);
homeViewModel.getMovies().observe(HomeActivity.this, new Observer<List<Movie>>() {
@Override
public void onChanged(@Nullable List<Movie> movieArrayList) {
String str = null;
if (movieArrayList != null) {
str = Arrays.toString(movieArrayList.toArray());
}
Log.e("movies", str);
}
});
}
}
サブクラスには、Applicationを唯一のパラメーターとして受け入れるコンストラクターが必要です。
コンストラクターがその要件を満たしていません。
どちらか:
HomeViewModel
から_Context context
_および_LifecycleOwner lifecycleOwner
_コンストラクターパラメーターを削除するか、
a _ViewModelProvider.Factory
_ を作成してHomeViewModel
インスタンスを構築し、ViewModelProviders.of()
でそのファクトリを使用します
Kotlinを使用しており、ViewModelコンストラクター内に依存関係を挿入してリポジトリのように動作してデータを取得する必要がある場合(Presenterレイヤーで使用するのと同じ方法)、以下を実行する必要があります。
データを取得するために、コンストラクターにUseCase/Interactorを挿入する必要があるViewModelがあるとします。
_class MainViewModel(private val itemList:ItemListUseCase):ViewModel() {
...
}
_
Activity/FragmentでこのViewModelをインスタンス化しようとすると、これを行う傾向があります。
フラグメントの例
_ class MainFragment : Fragment() {
private lateinit var mainViewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainViewModel = requireActivity().run {
ViewModelProviders.of(this).get(MainViewModel::class.Java)
}
...
}
}
_
コードを実行しようとすると、RuntimeException
でクラッシュします
Java.lang.RuntimeException:クラスcom.gaston.example.viewmodel.MainViewModelのインスタンスを作成できません...原因:Java.lang.InstantiationException:Java.lang.Classにはゼロ引数コンストラクタがありません
これは、ItemListUseCase
をインスタンス化するときにViewModel
を注入しないためです。
最初に出てくるのは、ViewModelProviders
の.get()
メソッドから直接注入しようとすることです
_ViewModelProviders.of(this).get(MainViewModel(ItemListUseCase())::class.Java)
_
しかし、これを行うと、同じエラーが発生します。
理解する必要があるのは
_ViewModelProviders.of(this).get(MainViewModel::class.Java)
_
ViewModelProviders.of()
は、コンストラクター内で依存関係が必要であることを知らずにMainViewModelのインスタンスを実行しようとしています。
この問題を修正するには、どの依存関係を注入するかをViewModelProviers.of()
に知らせる必要があります。
これを行うには、ViewModelのインスタンスがインスタンス化されるために依存関係を必要とすることを保証するクラスを作成する必要があります。
このクラスはFactory ViewModelクラスと呼ばれます。これは、動作する必要がある依存関係でViewModelを構築し、ViewModelProviers.of()
に.get(MainViewModel::class.Java)
に渡す必要がある依存関係を知らせるためです。
_class ViewModelFactory(val requestItemData: ItemListUseCase):ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return modelClass.getConstructor(ItemListUseCase::class.Java).newInstance(requestItemData)
}
}
_
これで、MainViewModel(ItemListuseCase())
をインスタンス化するために_ItemListUseCase::class.Java
_のインスタンスが必要であることをViewModelProviers.of()に伝えることができます
_override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainViewModel = requireActivity().run {
ViewModelProviders.of(this,ViewModelFactory(ItemListUseCase())).get(MainViewModel::class.Java)
}
}
_
注ファクトリーがこれらの引数をコンストラクターに注入するので、引数を.get(MainViewModel::class.Java)
に渡しません。
最後に、Factoryクラスを回避したい場合は、ViewModel Factoryクラスを気にすることなく、いつでもDaggerを使用して依存関係を注入できます。
それが誰かを助けるなら。私の場合、CommonsWareが言ったことはすべて正しいのですが、AppComponenntを挿入するのを忘れていました。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// this line was missing
((BaseApplication) getApplication()).getAppComponent().inject(this);
super.onCreate(savedInstanceState);
}
他の回答で述べたように、ViewModelProvidersはViewModelコンストラクターで必要なオブジェクトを注入できません。
ファクトリなしでViewModel quick and uglyを設定する必要がある場合は、生成されたDaggerActivityComponent
でgetYourViewModel()
メソッドを作成するDagger2を使用できます。 @Providesアノテーションで使用可能にしたコンストラクタにオブジェクトを追加します。次に、このフィールドをアクティビティに追加します。
_@Inject
lateinit var viewmodel: YourViewModel
_
OnCreate()
に注入します。これで終わりです。 [〜#〜] nb [〜#〜]:重いViewModelと遅いViewModelでは、このアプローチに注意してください。
これを追加してみてください:
private LiveData<Section[]> movies;
そして、これを変更します:
LiveData<List<Movie>> getMovies() {
if(movies==null)
movies= movieRepository.getMovies();
return movies;
}
それは私のために働いた。
In HomeViewModel class need to extend ViewModel class.
public class MovieListViewModel extends ViewModel {
private MutableLiveData<MovieModel> data;
private MovieRepository movieModel;
public MovieListViewModel() {
movieModel = new MovieRepository();
}
public void init() {
if (this.data != null) {
// ViewModel is created per Fragment so
// we know the userId won't change
return;
}
data = movieModel.getMovies();
}
public MutableLiveData<MovieModel> getMovies() {
return this.data;
}
}
https://github.com/kamydeep00178/androidMvvm 完全な例を確認できます