KoinをHiltに移行する作戦 〜これが終わったら俺結婚するんだ〜
やっぱりFragmentにKoinでViewModelをInjectしてViewModelにUseCaseをHiltで、他のクラスをKoinでInjectしてるとクラッシュしたけど、
FragmentにViewModelをInjectするのをHiltにしたらクラッシュしない
Hiltのコード生成はちゃんと動いてる
ViewPager(FragmentStateAdapter)内のFragmentで
ViewModelをHiltでInject
ViewModelにKoinComponentをつけて他クラスをKoinでInject
すると
Can't access ViewModels from detached fragment
が出てクラッシュする
ViewPagerとFragmentPagerAdapterの組み合わせだとクラッシュしない
ViewPager2とFragmentStateAdapterだとクラッシュする
小さい環境で調べたらViewPager2も関係なかった
何か別の原因があるっぽい
デバッグモードにしてみたらViewModelのインスタンスは作れてるからエラーメッセージと動作が合ってない・・・
これは勘違い
別のクラス見てた
Hilt化で顕在化したけど原因は別にありそう
KoinとHiltでインスタンス生成のタイミングが違うことが問題な様子
private val viewModel: MenuViewModel by viewModels()
private val adapter: Adapter(viewModel.flag)
としたい時にHiltだとFragmentが生成されていないのにViewModelを生成しようとしてクラッシュする
だけど、なんでKoinだと生成できるんだ???
※Hiltだとandroidx.app.Fragment内のチェック処理でFragmentManagerがnullだからクラッシュする
そもそもby viewModels()とかby androidViewModels()がandroidx.app.Fragment側の持ち物だからクラッシュしただけ
Hiltの機能だと思い込んでた
KoinのSharedViewModelはActivityに紐付くと何かで見たけど嘘な気がするな(バージョンによるかも)
HiltでactivityViewModelを使った時と動作違う気がする
勘違いで同じっぽい
ViewModelでAssistedInjectを使うとメモリリークするので良く無い
SavedStateHandleを経由して値を渡すようにするのがベター
参考情報
init {
_liveData.postValue("Test")
}
private val _liveData = MutableLiveData<String?>(null)
KoinでInjectすると↑は動くけど、HIltだと_liveDataのインスタンスが作られるのが遅れてクラッシュする
※他の条件があるかもしれない
KoinがFragment完成前に生成できる理由を追うためにアマゾンに行く
結論
androidx.lifecycle.ViewModelProviderを自前で生成してるから
※androidx.lifecycle.ViewModelProviderが完成すれば公式にgetできる
補足
fun <T : ViewModel> ViewModelStoreOwner.getViewModel(
clazz: KClass<T>,
qualifier: Qualifier? = null,
parameters: ParametersDefinition? = null
): T {
return getKoin().getViewModel(this, clazz, qualifier, parameters)
}
getViewModelの第一引数のthisにActivityやFragmentが入る
ViewModelProvider(@NonNull ViewModelStoreOwner owner)
でインスタンス生成できるのになんで周りくどいことしてるんだろ???
Hiltの生成
Fragment.ViewModelProvider.Factory getDefaultViewModelProviderFactory()
を呼んでる
if (mFragmentManager == null) {
throw new IllegalStateException("Can't access ViewModels from detached fragment");
}
@MainThread
fun <VM : ViewModel> Fragment.createViewModelLazy(
viewModelClass: KClass<VM>,
storeProducer: () -> ViewModelStore,
factoryProducer: (() -> Factory)? = null
): Lazy<VM> {
val factoryPromise = factoryProducer ?: {
defaultViewModelProviderFactory
}
return ViewModelLazy(viewModelClass, storeProducer, factoryPromise)
}
ここからクラッシュするところ呼んでる
mFragmentManagerはFragmentTransactionに追加される時に設定される