概要
Android超絶初心者の僕がkotlinでViewModelをDIするときに苦戦したのでメモ程度にViewModelをDIする方法を書きます。
全体設計
今回作っていたのでTodoリストのようなもので
めちゃくちゃざっくりした設計はこんな感じです。
今回やりたかったこと
TodoViewModelからFirestoreRepositoryをDIしたかった。
普段のDI
firestoreRepositoryからFirebaseFirestoreをDIするときは
class FirestoreRepository(val db: FirebaseFirestore = FirebaseFirestore.getInstance()) {
こんな感じでかけていたので特に苦戦することはなかったのですが....
詰まった点
同じようにTodoViewModelの方で
class TodoViewModel(
application: Application,
private val firestoreRepository: FirestoreRepository = FirestoreRepository()
) : AndroidViewModel(application) {
こんな感じで書いた所、実行時に
Process: com.example.myapplication, PID: 12747
java.lang.RuntimeException: Cannot create an instance of class com.example.myapplication.TodoViewModel
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:269)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.example.myapplication.fragment.IndexFragment.onCreateView(IndexFragment.kt:31)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:310)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1185)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1354)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1432)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1495)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447)
at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2167)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1945)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1847)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.lang.NoSuchMethodException: com.example.myapplication.TodoViewModel.<init> [class android.app.Application]
at java.lang.Class.getConstructor0(Class.java:2332)
at java.lang.Class.getConstructor(Class.java:1728)
at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.java:267)
at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:106)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:185)
at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.java:150)
at com.example.myapplication.fragment.IndexFragment.onCreateView(IndexFragment.kt:31)
at androidx.fragment.app.Fragment.performCreateView(Fragment.java:2698)
at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:310)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1185)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1354)
at androidx.fragment.app.FragmentManager.moveFragmentToExpectedState(FragmentManager.java:1432)
at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1495)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:447)
at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2167)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1945)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1847)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
このようなエラーが吐かれてしまった...
解決策
Factoryクラスなるものを作ればいけるとのこと。
基本的にここに書かれてるんですけど、Javaなんですよね...
https://stackoverflow.com/questions/46283981/android-viewmodel-additional-arguments
kotlinバージョンだと
Factoryクラスは
package com.example.myapplication
import android.app.Application
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
class TodoViewModelFactory(
private var application: Application
) : ViewModelProvider.Factory {
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
return TodoViewModel(application) as T
}
}
こんな感じで、呼び出し側は
viewModel = ViewModelProvider(
requireActivity(),
TodoViewModelFactory(mainActivity.application)
).get(TodoViewModel::class.java)
こんな感じになるかと
解決。
Dagger?とか使えばこれも簡単らしい...???