(AssistedInject を使った方法はぐぐれば出てくるのでぐぐってください)
この内容は技術書典7で頒布予定の Master of Dagger(仮)の正式版にも掲載予定です。
ここには詳しい解説は書かないので、ぜひ Master of Dagger(仮)の正式版を買ってください。
ポイントはコンストラクタで先ほど作った SavedStateViewModelComponent.Factory を受け取るようにし、create() でそれを利用して ViewModel の Map Multibindings を受け取るようにすることです。
SavedStateComponent の modules に SavedStateViewModelComponentModule を指定します。
この内容は技術書典7で頒布予定の Master of Dagger(仮)の正式版にも掲載予定です。
ここには詳しい解説は書かないので、ぜひ Master of Dagger(仮)の正式版を買ってください。
0.
このような ViewModel があります。(Dagger で生成させてる MainViewModel のコンストラクタに SavedStateHandle を追加した)class MainViewModel @Inject constructor( private val api: MyApi, private val handle: SavedStateHandle ) : ViewModel() { ... }
MyApi は Dagger で管理しています。 @Module class AppModule { @Singleton @Provides fun provideApi(): MyApi { return ... } }
MainViewModel は ViewModel の Map Multibindings の1要素になっています。 @Module interface BindModule { @Binds @IntoMap @ViewModelKey(MainViewModel::class) fun bindMainViewModel(viewModel: MainViewModel): ViewModel }
1.
SavedStateHandle をオブジェクトグラフに含む Subcomponent を用意します。@Module(subcomponents = [SavedStateViewModelComponent::class]) class SavedStateViewModelComponentModule @Subcomponent interface SavedStateViewModelComponent { @Subcomponent.Factory interface Factory { fun create( @BindsInstance handle: SavedStateHandle ): SavedStateViewModelComponent } fun providers(): Map<Class<out ViewModel>, Provider<ViewModel>> }
2.
次に AbstractSavedStateViewModelFactory を継承した Factory を用意します。ポイントはコンストラクタで先ほど作った SavedStateViewModelComponent.Factory を受け取るようにし、create() でそれを利用して ViewModel の Map Multibindings を受け取るようにすることです。
class SavedStateViewModelFactory @Inject constructor( private val factory: SavedStateViewModelComponent.Factory, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) { override fun <T : ViewModel?> create( key: String, modelClass: Class<T>, handle: SavedStateHandle ): T { val providers = factory.create(handle).providers() val found = providers.entries.find { modelClass.isAssignableFrom(it.key) } val provider = found?.value ?: throw IllegalArgumentException("unknown model class $modelClass") try { @Suppress("UNCHECKED_CAST") return provider.get() as T } catch (e: Exception) { throw RuntimeException(e) } } }
3.
SavedStateRegistryOwner と Bundle? をオブジェクトグラフに含む Subcomponent を用意します。SavedStateComponent の modules に SavedStateViewModelComponentModule を指定します。
@Module(subcomponents = [SavedStateComponent::class]) class SavedStateComponentModule @Subcomponent(modules = [SavedStateViewModelComponentModule::class]) interface SavedStateComponent { @Subcomponent.Factory interface Factory { fun create( @BindsInstance owner: SavedStateRegistryOwner, @BindsInstance defaultArgs: Bundle? ): SavedStateComponent } fun viewModelFactory(): SavedStateViewModelFactory }
4.
AppComponent の modules に SavedStateComponentModule を指定します。@Singleton @Component( modules = [ AppModule::class, BindModule::class, SavedStateComponentModule::class ] ) interface AppComponent { fun savedStateComponentFactory(): SavedStateComponent.Factory }
5.
SavedStateComponentから取得したFactoryを使います。class MainActivity : AppCompatActivity() { private val viewModel: MainViewModel by viewModels { (application as MyApplication).appComponent .savedStateComponentFactory() .create(this, null) .viewModelFactory() } ... }