2019年8月5日月曜日

SavedStateHandle を Dagger で生成させてる ViewModel で使う方法(AssistedInject を使わない方法)

(AssistedInject を使った方法はぐぐれば出てくるのでぐぐってください)

この内容は技術書典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() } ... }

0 件のコメント:

コメントを投稿