2020年5月27日水曜日

Dagger に Fragment と FragmentFactory の生成をまかせる

Master of Dagger の改定版にも入れる予定です。ただいま鋭意執筆中です。もう少々お待ちください。


ViewModelFactory と同じような感じで FragmentFactory および Fragment の生成をまかせることができます。

オブジェクトグラフに MyApi があるとします。
  1. @Module  
  2. object AppModule {  
  3.   
  4.     @Provides  
  5.     fun provideMyApi(): MyApi {  
  6.         ...  
  7.     }  
  8. }  
これを引数にとる Fragment があります。Dagger に生成をまかせたいのでコンストラクタに @Inject をつけます。
  1. class MainFragment @Inject constructor(private val api: MyApi) : Fragment() {  
  2.   
  3.     ...  
  4. }  
Fragment の Map Multibindings 用の MapKey を用意します。
  1. @Target(  
  2.     AnnotationTarget.FUNCTION,  
  3.     AnnotationTarget.PROPERTY_GETTER,  
  4.     AnnotationTarget.PROPERTY_SETTER  
  5. )  
  6. @Retention(AnnotationRetention.RUNTIME)  
  7. @MapKey  
  8. annotation class FragmentKey(val value: KClass<out Fragment>)  
用意した MapKey を使って MainFragment を Multibindings に追加します。
  1. @Module  
  2. interface FragmentModule {  
  3.   
  4.     @Binds  
  5.     @IntoMap  
  6.     @FragmentKey(MainFragment::class)  
  7.     fun bindMainFragment(fragment: MainFragment): Fragment  
  8. }  
Fragment の Multibindings を引数に取る FragmentFactory を用意します。
  1. class MyFragmentFactory @Inject constructor(  
  2.     private val providers: Map<Class<out Fragment>, @JvmSuppressWildcards Provider<Fragment>>  
  3. ) : FragmentFactory() {  
  4.   
  5.     override fun instantiate(classLoader: ClassLoader, className: String): Fragment {  
  6.         val found = providers.entries.find { className == it.key.name }  
  7.             ?: throw IllegalArgumentException("unknown model class $className")  
  8.   
  9.         val provider = found.value  
  10.   
  11.         try {  
  12.             @Suppress("UNCHECKED_CAST")  
  13.             return provider.get()  
  14.         } catch (e: Exception) {  
  15.             return super.instantiate(classLoader, className)  
  16.         }  
  17.     }  
  18. }  
用意した MyFragmentFactory を取得するためのメソッドを Component に用意します。
  1. @Component(modules = [AppModule::class, FragmentModule::class])  
  2. interface AppComponent {  
  3.   
  4.     fun fragmentFactory(): MyFragmentFactory  
  5. }  
  6.   
  7. class MyApplication : Application() {  
  8.   
  9.     lateinit var appComponent: AppComponent  
  10.   
  11.     override fun onCreate() {  
  12.         super.onCreate()  
  13.   
  14.         appComponent = DaggerAppComponent.builder()  
  15.             .build()  
  16.     }  
  17. }  
supportFragmentManager.fragmentFactory に Component から取得した MyFragmentFactory をセットします。
  1. class MainActivity : AppCompatActivity() {  
  2.   
  3.     override fun onCreate(savedInstanceState: Bundle?) {  
  4.         super.onCreate(savedInstanceState)  
  5.   
  6.         supportFragmentManager.fragmentFactory = (application as MyApplication).appComponent  
  7.             .fragmentFactory()  
  8.   
  9.         setContentView(R.layout.activity_main)  
  10.     }  
  11. }  
activity_main.xml
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <fragment xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     android:id="@+id/mainFragment"  
  4.     android:name="net.yanzm.sample.MainFragment"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent" />  



0 件のコメント:

コメントを投稿