AndroidのViewModelはDaggerで安全にAssisted Injectできるのか
結論から言うと現時点では難しいのではないかと思います 前提としてDagger Hiltを使うことを想定しています
ここでいう「安全に」とはDIするインスタンスが適切なComponentで管理されていることを指します
HiltではActivityならActivityComponent、ViewModelならViewModelComponentといったように、生存期間の違いに応じて自動的に異なるComponentでインスタンスが管理されます
https://dagger.dev/hilt/components.html
Daggerを使ってViewModelをAssisted Injectする場合、@HiltViewModelを同時につか
Daggerを使ってViewModelをAssisted Injectするコードはビルドもできますし、インスタンスが注入されもします
しかし、ViewModelをAssisted Injectすると本来ViewModelComponentで管理したいところがActivityComponentあるいはFragmentComponentの管理下に置かれます(これはViewModelをどこで保持しているかによります)
これのデメリットとして、
- ViewModelにViewModelScopedのインスタンスがDIできない
- ViewModelにActivityScopedやFragmentScopedのインスタンスがDIできてしまう
ことが挙げられます
特に2では、生存期間が比較的長いViweModelから生存期間が短いインスタンスを保持できてしまうのでメモリリークの可能性があります
ActivityScopedなインスタンスでActivityをDIしていたりすると気づかないうちにActivityがリークしているということが起こりえます
さらに、Activityが再生成されたときにViewModelが保持しているインスタンスが残ったままになる問題が起こります
例えばこのようなコードが書けてしまい、Activityの再生成前はActivityとViewModelで同じActivityScopedDataのインスタンスを参照しますが、再生成された後は異なるインスタンスを参照します
|
|
これはActivityScopedDataを@ActivityRetainedScopedで管理すれば同じインスタンスになるので注意して書けば意図通り動くでしょう
しかし、ViewModelがViewModelComponentで管理できていればビルドエラーで気付ける問題なので比較的危険な状態であるといえます
すでにHiltViewModelかつAssisted Injectをサポートしてほしい旨の議論は下記のissueでなされていますが、いまのところ進展はないようです
https://github.com/google/dagger/issues/2287
代替案としてSavedStateHandleをViewModelにDIしてそこから値を取得する方法が挙げられています
SavedStateHandleを使うにしてもgetするときにnullableでとれるのでそこに目をつぶればこちらでもいい気はします
ViewModelのAssisted Injectサポートは個人的にはあるととても嬉しい機能なので期待したいです