例えば、動画を再生しているアプリで、このVideoのIDの動画を再生したい!みたいなときにViewModelにidを渡したくなったりしますよね?そしてそのViewModelにRepositoryとかのインスタンスをinjectしつつ、videoIdを渡してvideoIdがNullableであるとかlateinitであるとかを意識せずに簡単に使えるようにしたいですよね。これができるのがAssited Injectです。
これを使うとコンストラクタに自分で指定した引数を追加することができるのでNullableやlateinitにせずに引数を追加できます。
この用途に、これまでsquare/AssistedInjectを使っていましたが、Dagger 2.31でこの機能が追加されました。
コード
基本的にinterfaceでFactoryを作ってそれに@AssistedFactory
をつけて、そこに渡したい引数を追加したメソッドを追加し、作りたいクラスのコンストラクタに@AssistedInject
をつけて、カスタマイズしたい引数に@Assisted
をつけるだけです。
class VideoPlayerViewModel @AssistedInject constructor(
private val videoPlayer: VideoPlayer,
@Assisted private val videoId: String
) : ViewModel() {
fun play() {
videoPlayer.play()
}
@AssistedFactory
interface Factory {
fun create(videoId: String): VideoPlayerViewModel
}
}
そして、FactoryをActivityやFragmentで以下のようにinjectさせてあげるだけです。
@Inject
lateinit var videoPlayerViewModelAssistedFactory: VideoPlayerViewModel.Factory
あとはvideoPlayerViewModelAssistedFactory.create(videoId)とかで呼び出せばViewModelが作れます。
Assisted Injectの説明としてはこれで問題ないのですが、注意が必要な点として、ViewModelは自由にインスタンスを作っていいものではなく、画面回転でもViewModelStoreで保持されるようにする必要があるため、FragmentなどのProviderを利用して取得する必要があるということです。
サンプル
square/AssistedInjetから移行する完全なサンプルは以下です。以下ではby viewModels{}
を使うことでViewModelStoreで保持させつつ、AssitedInjectを利用しています。
square/AssistedInjetと違う点としてAssistedModule
というのを作る必要がなくなったので非常にシンプルに利用できるようになったので、とても使いやすいです。
https://github.com/takahirom/hilt-sample-app/commit/f54e58d72057e8d81921fa0d4e3bcfe809e9c5b9