无法创建 ViewModel 的实例 class(无法启动 activity ComponentInfo)
Cannot create an instance of ViewModel class (Unable to start activity ComponentInfo)
我在我的项目中使用 MVVM、Retrofit、LiveData,但在我看到这些链接之前出现此错误
错误
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();
} }
Activity class
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);
}
});
}
}
我应该在我的项目和自定义工厂中使用 Dagger 吗?
引用 the documentation for AndroidViewModel
:
Subclasses must have a constructor which accepts Application as the only parameter.
您的构造函数不符合该要求。
或者:
从 HomeViewModel
中删除 Context context
和 LifecycleOwner lifecycleOwner
构造函数参数,或者
创建可以构建您的 HomeViewModel
实例的 a ViewModelProvider.Factory
,并将该工厂与 ViewModelProviders.of()
一起使用
基于this answer,你可以这样做
class MvpApp : Application(){
companion object {
lateinit var application: Application
}
override fun onCreate() {
super.onCreate()
application = this
}
}
然后:
class ProfileViewModel: AndroidViewModel(MvpApp.application) {}
如果对某人有帮助。在我的例子中,CommonsWare 告诉我的一切都是正确的,但是,我忘了注入 AppComponennt。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// this line was missing
((BaseApplication) getApplication()).getAppComponent().inject(this);
super.onCreate(savedInstanceState);
}
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;
}
}
尝试添加这个:
private LiveData<Section[]> movies;
然后,改变这个:
LiveData<List<Movie>> getMovies() {
if(movies==null)
movies= movieRepository.getMovies();
return movies;
}
它对我有用。
正如其他答案中提到的,ViewModelProviders 无法在 ViewModel 构造函数中注入您需要的对象。
如果您需要在没有任何工厂的情况下设置 ViewModel 快速且丑陋,那么您可以使用 Dagger2,它会在生成的 [=12= 中创建 getYourViewModel()
方法],同时将这些对象放入您使用 @Provides 注释提供的构造函数中。然后只需将此字段添加到您的 activity:
@Inject
lateinit var viewmodel: YourViewModel
并将其注入 OnCreate()
即可。 注意:对于繁重且缓慢的 ViewModel,请谨慎使用此方法。
如果您正在使用 Kotlin 并且需要在 ViewModel 构造函数中注入依赖项以像 Repository 一样使用以获取数据(与我们使用 Presenter 层的方式相同),您将需要执行以下操作正在关注。
假设我们有一个 ViewModel,需要将 UseCase/Interactor 注入到构造函数中以从中获取数据。
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 {
ViewModelProvider(this).get(MainViewModel::class.java)
}
...
}
}
当我们尝试 运行 我们的代码时,它会崩溃 RuntimeException
java.lang.RuntimeException: Cannot create an instance of class
com.gaston.example.viewmodel.MainViewModel ... Caused by:
java.lang.InstantiationException:
java.lang.Class<com.gaston.example.viewmodel.MainViewModel>
has no zero argument constructor
这是因为我们在实例化 ViewModel
时没有注入 ItemListUseCase
首先想到的是尝试直接从ViewModelProvider
的.get()
方法注入
ViewModelProvider(this).get(MainViewModel(ItemListUseCase()))
但是如果我们这样做,我们也会得到同样的错误。
解决方案
我们需要明白的是
ViewModelProvider(this).get(MainViewModel::class.java)
ViewModelProvider()
正在尝试执行 MainViewModel 的实例,但不知道它需要在其构造函数中依赖。
要解决此问题,我们需要让 ViewModelProvier()
了解我们要注入的依赖项。
为此,我们需要创建一个 class 以确保该 ViewModel 的实例需要实例化的依赖项。
这个 class 被称为 Factory ViewModel class,因为它将构建我们的 ViewModel 以及它需要工作的依赖项,然后它会让 ViewModelProvier()
知道我们使用哪个依赖项需要传递给 .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)
}
}
现在,我们可以告诉 ViewModelProviers.of() 它需要一个 ItemListUseCase::class.java
的实例来实例化 MainViewModel(ItemListuseCase())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainViewModel = requireActivity().run {
ViewModelProvider(this,ViewModelFactory(ItemListUseCase())).get(MainViewModel::class.java)
}
}
注意 我没有将任何参数传递给 .get(MainViewModel::class.java)
因为我们的工厂会负责将这些参数注入我们的构造函数。
最后,如果你想避免工厂 class,你总是可以使用 Dagger 注入你的依赖而不用担心 ViewModel 工厂 class。
我在我的项目中使用 MVVM、Retrofit、LiveData,但在我看到这些链接之前出现此错误
错误
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();
} }
Activity class
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);
}
});
}
}
我应该在我的项目和自定义工厂中使用 Dagger 吗?
引用 the documentation for AndroidViewModel
:
Subclasses must have a constructor which accepts Application as the only parameter.
您的构造函数不符合该要求。
或者:
从
HomeViewModel
中删除Context context
和LifecycleOwner lifecycleOwner
构造函数参数,或者创建可以构建您的
HomeViewModel
实例的 aViewModelProvider.Factory
,并将该工厂与ViewModelProviders.of()
一起使用
基于this answer,你可以这样做
class MvpApp : Application(){
companion object {
lateinit var application: Application
}
override fun onCreate() {
super.onCreate()
application = this
}
}
然后:
class ProfileViewModel: AndroidViewModel(MvpApp.application) {}
如果对某人有帮助。在我的例子中,CommonsWare 告诉我的一切都是正确的,但是,我忘了注入 AppComponennt。
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
// this line was missing
((BaseApplication) getApplication()).getAppComponent().inject(this);
super.onCreate(savedInstanceState);
}
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;
}
}
尝试添加这个:
private LiveData<Section[]> movies;
然后,改变这个:
LiveData<List<Movie>> getMovies() {
if(movies==null)
movies= movieRepository.getMovies();
return movies;
}
它对我有用。
正如其他答案中提到的,ViewModelProviders 无法在 ViewModel 构造函数中注入您需要的对象。
如果您需要在没有任何工厂的情况下设置 ViewModel 快速且丑陋,那么您可以使用 Dagger2,它会在生成的 [=12= 中创建 getYourViewModel()
方法],同时将这些对象放入您使用 @Provides 注释提供的构造函数中。然后只需将此字段添加到您的 activity:
@Inject
lateinit var viewmodel: YourViewModel
并将其注入 OnCreate()
即可。 注意:对于繁重且缓慢的 ViewModel,请谨慎使用此方法。
如果您正在使用 Kotlin 并且需要在 ViewModel 构造函数中注入依赖项以像 Repository 一样使用以获取数据(与我们使用 Presenter 层的方式相同),您将需要执行以下操作正在关注。
假设我们有一个 ViewModel,需要将 UseCase/Interactor 注入到构造函数中以从中获取数据。
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 {
ViewModelProvider(this).get(MainViewModel::class.java)
}
...
}
}
当我们尝试 运行 我们的代码时,它会崩溃 RuntimeException
java.lang.RuntimeException: Cannot create an instance of class com.gaston.example.viewmodel.MainViewModel ... Caused by: java.lang.InstantiationException: java.lang.Class<com.gaston.example.viewmodel.MainViewModel> has no zero argument constructor
这是因为我们在实例化 ViewModel
ItemListUseCase
首先想到的是尝试直接从ViewModelProvider
.get()
方法注入
ViewModelProvider(this).get(MainViewModel(ItemListUseCase()))
但是如果我们这样做,我们也会得到同样的错误。
解决方案
我们需要明白的是
ViewModelProvider(this).get(MainViewModel::class.java)
ViewModelProvider()
正在尝试执行 MainViewModel 的实例,但不知道它需要在其构造函数中依赖。
要解决此问题,我们需要让 ViewModelProvier()
了解我们要注入的依赖项。
为此,我们需要创建一个 class 以确保该 ViewModel 的实例需要实例化的依赖项。
这个 class 被称为 Factory ViewModel class,因为它将构建我们的 ViewModel 以及它需要工作的依赖项,然后它会让 ViewModelProvier()
知道我们使用哪个依赖项需要传递给 .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)
}
}
现在,我们可以告诉 ViewModelProviers.of() 它需要一个 ItemListUseCase::class.java
的实例来实例化 MainViewModel(ItemListuseCase())
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainViewModel = requireActivity().run {
ViewModelProvider(this,ViewModelFactory(ItemListUseCase())).get(MainViewModel::class.java)
}
}
注意 我没有将任何参数传递给 .get(MainViewModel::class.java)
因为我们的工厂会负责将这些参数注入我们的构造函数。
最后,如果你想避免工厂 class,你总是可以使用 Dagger 注入你的依赖而不用担心 ViewModel 工厂 class。