无法创建ViewModel类的实例(无法启动活动ComponentInfo)

Sin*_*imi 6 android mvvm android-viewmodel android-architecture-components

我在我的项目中使用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
Run Code Online (Sandbox Code Playgroud)

这是我的ViewModel,我认为问题出在我的构造函数中

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();
}}
Run Code Online (Sandbox Code Playgroud)

知识库

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();
} }
Run Code Online (Sandbox Code Playgroud)

活动类

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);
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

}

我应该在我的项目和定制工厂中使用Dagger吗?

Com*_*are 11

引用文档AndroidViewModel:

子类必须有一个构造函数,它接受Application作为唯一的参数.

您的构造函数不符合该要求.

或者:

  • 从您的或中删除Context contextLifecycleOwner lifecycleOwner构造函数参数HomeViewModel

  • 创建一个ViewModelProvider.Factory可以构建您的HomeViewModel实例,并使用该工厂ViewModelProviders.of()


Gas*_*lén 5

如果您正在使用 Kotlin,并且需要在 ViewModel 构造函数中注入一个依赖项,以便像存储库一样使用以获取数据(与我们使用 Presenter 层的方式相同),您将需要执行以下操作。

假设我们有一个 ViewModel,它需要一个 UseCase/Interactor 被注入到构造函数中以从中获取数据。

class MainViewModel(private val itemList:ItemListUseCase):ViewModel() {
...
}
Run Code Online (Sandbox Code Playgroud)

当我们尝试在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)
            }
          ...
        }
}
Run Code Online (Sandbox Code Playgroud)

当我们尝试运行我们的代码时,它会崩溃 RuntimeException

java.lang.RuntimeException: 无法创建类 com.gaston.example.viewmodel.MainViewModel 的实例...引起:java.lang.InstantiationException: java.lang.Class<com.gaston.example.viewmodel.MainViewModel> 有无零参数构造函数

这是因为我们ItemListUseCase在实例化我们的时候没有注入ViewModel

在出现的第一件事就是尝试直接从注入它.get()的方法ViewModelProvider

ViewModelProvider(this).get(MainViewModel(ItemListUseCase()))
Run Code Online (Sandbox Code Playgroud)

但是如果我们这样做,我们也会得到同样的错误。

解决方案

我们需要明白的是

ViewModelProvider(this).get(MainViewModel::class.java)
Run Code Online (Sandbox Code Playgroud)

ViewModelProvider() 正在尝试执行 MainViewModel 的实例而不知道它在其构造函数中需要依赖项。

为了解决这个问题,我们需要ViewModelProvier()知道我们想要注入哪个依赖项。

为此,我们需要创建一个类,以确保该 ViewModel 的实例需要实例化依赖项。

这个类被称为 Factory ViewModel 类,因为它将使用它需要工作的依赖来构建我们的 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,我们可以告诉 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)
        }

    }
Run Code Online (Sandbox Code Playgroud)

请注意,我没有向 传递任何参数,.get(MainViewModel::class.java)因为我们的工厂会负责将这些参数注入到我们的构造函数中。

最后,如果您想避免使用 Factory 类,您可以随时使用 Dagger 注入您的依赖项,而无需担心 ViewModel Factory 类。