11

我在我的项目中使用 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();
} }

活动课

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 吗?

4

7 回答 7

15

引用文档AndroidViewModel

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

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

任何一个:

  • 从您的Context context,或LifecycleOwner lifecycleOwnerHomeViewModel

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

于 2018-02-04T13:36:21.820 回答
5

如果您正在使用 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:无法创建 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()))

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

解决方案

我们需要明白的是

ViewModelProvider(this).get(MainViewModel::class.java)

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

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

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

这个类被称为工厂 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)
    }
}

现在,我们可以告诉 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)因为我们的工厂将负责将这些参数注入到我们的构造函数中。

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

于 2019-08-08T19:34:55.100 回答
1

尝试添加这个:

private LiveData<Section[]> movies;

然后,改变这个:

LiveData<List<Movie>> getMovies() {
    if(movies==null)
        movies= movieRepository.getMovies();
    return movies;
}

它对我有用。

于 2018-09-27T21:54:47.010 回答
0

如果它对某人有帮助。就我而言,CommonsWare 所说的一切都是正确的,但是,我忘记注入 AppComponennt。

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        // this line was missing
        ((BaseApplication) getApplication()).getAppComponent().inject(this); 
        super.onCreate(savedInstanceState);
}
于 2018-09-21T11:35:32.647 回答
0
    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也可以查看完整示例

于 2018-09-21T12:00:28.523 回答
0

正如其他答案中提到的那样,ViewModelProviders 无法在 ViewModel 构造函数中注入您需要的对象。

如果您需要在没有任何工厂的情况下快速而丑陋地设置 ViewModel ,那么您可以使用 Dagger2getYourViewModel()在 generated 中创建方法DaggerActivityComponent,同时将这些对象放入您使用 @Provides 注释提供的构造函数中。然后只需将此字段添加到您的活动中:

@Inject
lateinit var viewmodel: YourViewModel

并注入它OnCreate(),就是这样。注意:对于重型和缓慢的 ViewModel 使用这种方法时要小心。

于 2019-05-10T08:06:24.423 回答
-3

基于这个答案,你可以这样做

class MvpApp : Application(){

    companion object {
        lateinit var application: Application
    }

    override fun onCreate() {
        super.onCreate()
        application = this
    }
}

进而:

class ProfileViewModel: AndroidViewModel(MvpApp.application) {}
于 2018-06-19T12:46:11.120 回答