web-dev-qa-db-ja.com

ViewModelクラスのインスタンスを作成できません(アクティビティ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

これは私の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();
}}

リポジトリ

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を使用する必要がありますか?

6
Sina Rahimi

引用 AndroidViewModelのドキュメント

サブクラスには、Applicationを唯一のパラメーターとして受け入れるコンストラクターが必要です。

コンストラクターがその要件を満たしていません。

どちらか:

  • HomeViewModelから_Context context_および_LifecycleOwner lifecycleOwner_コンストラクターパラメーターを削除するか、

  • a _ViewModelProvider.Factory_ を作成してHomeViewModelインスタンスを構築し、ViewModelProviders.of()でそのファクトリを使用します

11
CommonsWare

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を使用して依存関係を注入できます。

0

それが誰かを助けるなら。私の場合、CommonsWareが言ったことはすべて正しいのですが、AppComponenntを挿入するのを忘れていました。

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        // this line was missing
        ((BaseApplication) getApplication()).getAppComponent().inject(this); 
        super.onCreate(savedInstanceState);
}
0
Soon Santos

他の回答で述べたように、ViewModelProvidersはViewModelコンストラクターで必要なオブジェクトを注入できません。

ファクトリなしでViewModel quick and uglyを設定する必要がある場合は、生成されたDaggerActivityComponentgetYourViewModel()メソッドを作成するDagger2を使用できます。 @Providesアノテーションで使用可能にしたコンストラクタにオブジェクトを追加します。次に、このフィールドをアクティビティに追加します。

_@Inject
lateinit var viewmodel: YourViewModel
_

OnCreate()に注入します。これで終わりです。 [〜#〜] nb [〜#〜]:重いViewModelと遅いViewModelでは、このアプローチに注意してください。

0
soshial

これを追加してみてください:

private LiveData<Section[]> movies;

そして、これを変更します:

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

それは私のために働いた。

0
Jhon Paul
    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 完全な例を確認できます

0
Kamal Kakkar