2019年8月5日月曜日

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

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

この内容は技術書典7で頒布予定の Master of Dagger(仮)の正式版にも掲載予定です。
ここには詳しい解説は書かないので、ぜひ Master of Dagger(仮)の正式版を買ってください。



0.

このような ViewModel があります。(Dagger で生成させてる MainViewModel のコンストラクタに SavedStateHandle を追加した)
  1. class MainViewModel @Inject constructor(  
  2.     private val api: MyApi,  
  3.     private val handle: SavedStateHandle  
  4. ) : ViewModel() {  
  5.     ...  
  6. }  
MyApi は Dagger で管理しています。
  1. @Module  
  2. class AppModule {  
  3.   
  4.     @Singleton  
  5.     @Provides  
  6.     fun provideApi(): MyApi {  
  7.         return ...  
  8.     }  
  9. }  
MainViewModel は ViewModel の Map Multibindings の1要素になっています。
  1. @Module  
  2. interface BindModule {  
  3.   
  4.     @Binds  
  5.     @IntoMap  
  6.     @ViewModelKey(MainViewModel::class)  
  7.     fun bindMainViewModel(viewModel: MainViewModel): ViewModel  
  8. }  

1.

SavedStateHandle をオブジェクトグラフに含む Subcomponent を用意します。
  1. @Module(subcomponents = [SavedStateViewModelComponent::class])  
  2. class SavedStateViewModelComponentModule  
  3.   
  4. @Subcomponent  
  5. interface SavedStateViewModelComponent {  
  6.   
  7.     @Subcomponent.Factory  
  8.     interface Factory {  
  9.         fun create(  
  10.             @BindsInstance handle: SavedStateHandle  
  11.         ): SavedStateViewModelComponent  
  12.     }  
  13.   
  14.     fun providers(): Map<Class<out ViewModel>, Provider<ViewModel>>  
  15. }  

2.

次に AbstractSavedStateViewModelFactory を継承した Factory を用意します。

ポイントはコンストラクタで先ほど作った SavedStateViewModelComponent.Factory を受け取るようにし、create() でそれを利用して ViewModel の Map Multibindings を受け取るようにすることです。
  1. class SavedStateViewModelFactory @Inject constructor(  
  2.     private val factory: SavedStateViewModelComponent.Factory,  
  3.     owner: SavedStateRegistryOwner,  
  4.     defaultArgs: Bundle? = null  
  5. ) : AbstractSavedStateViewModelFactory(owner, defaultArgs) {  
  6.   
  7.     override fun <T : ViewModel?> create(  
  8.         key: String,  
  9.         modelClass: Class<T>,  
  10.         handle: SavedStateHandle  
  11.     ): T {  
  12.         val providers = factory.create(handle).providers()  
  13.   
  14.         val found = providers.entries.find { modelClass.isAssignableFrom(it.key) }  
  15.         val provider = found?.value  
  16.             ?: throw IllegalArgumentException("unknown model class $modelClass")  
  17.   
  18.         try {  
  19.             @Suppress("UNCHECKED_CAST")  
  20.             return provider.get() as T  
  21.         } catch (e: Exception) {  
  22.             throw RuntimeException(e)  
  23.         }  
  24.     }  
  25. }  

3.

SavedStateRegistryOwner と Bundle? をオブジェクトグラフに含む Subcomponent を用意します。
SavedStateComponent の modules に SavedStateViewModelComponentModule を指定します。
  1. @Module(subcomponents = [SavedStateComponent::class])  
  2. class SavedStateComponentModule  
  3.   
  4. @Subcomponent(modules = [SavedStateViewModelComponentModule::class])  
  5. interface SavedStateComponent {  
  6.   
  7.     @Subcomponent.Factory  
  8.     interface Factory {  
  9.         fun create(  
  10.             @BindsInstance owner: SavedStateRegistryOwner,  
  11.             @BindsInstance defaultArgs: Bundle?  
  12.         ): SavedStateComponent  
  13.     }  
  14.   
  15.     fun viewModelFactory(): SavedStateViewModelFactory  
  16. }  

4.

AppComponent の modules に SavedStateComponentModule を指定します。
  1. @Singleton  
  2. @Component(  
  3.     modules = [  
  4.         AppModule::class,  
  5.         BindModule::class,  
  6.         SavedStateComponentModule::class  
  7.     ]  
  8. )  
  9. interface AppComponent {  
  10.   
  11.     fun savedStateComponentFactory(): SavedStateComponent.Factory  
  12. }  

5.

SavedStateComponentから取得したFactoryを使います。
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     private val viewModel: MainViewModel by viewModels {  
  4.         (application as MyApplication).appComponent  
  5.             .savedStateComponentFactory()  
  6.             .create(thisnull)  
  7.             .viewModelFactory()  
  8.     }  
  9.   
  10.     ...  
  11. }  

0 件のコメント:

コメントを投稿