2020年5月27日水曜日

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

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


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

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


1 件のコメント: