この内容は技術書典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 件のコメント:
コメントを投稿