Dagger 2作用域与多绑定:Android简化API定制难题咨询
我之前特别吃Dagger 2 Android那个全新简化API的安利,用起来贼省心!但最近要做一点点定制化的时候,却直接卡壳了——相信很多做多Activity应用的同学都遇到过类似场景:整个App里的所有依赖都是通过Dagger注入的,现在想给某个Activity的注入流程加一点自定义逻辑,却不知道从哪儿下手。
先跟大家唠唠我当前的实现:
我首先创建了常规的ActivitiesModule,代码大概是这样的:
@Module abstract class ActivitiesModule { @PerActivity @ContributesAndroidInjector(modules = [MainActivityModule::class]) abstract fun contributeMainActivityInjector(): MainActivity // 其他Activity的注入声明,比如DetailActivity之类的 // @PerActivity // @ContributesAndroidInjector(modules = [DetailActivityModule::class]) // abstract fun contributeDetailActivityInjector(): DetailActivity }
这种简化写法确实爽,不用手动写Subcomponent和Builder,Dagger自动帮我生成所有代码,注入Activity也只需要在onCreate里调用AndroidInjection.inject(this)就行。
但现在我需要给某个Activity的注入做定制——比如我想在注入的时候动态传递一个参数(比如从Intent里拿到的用户ID),或者想让生成的Subcomponent暴露一些依赖给其他组件用,这时候@ContributesAndroidInjector的自动生成逻辑就不够灵活了,完全不知道怎么加自定义逻辑进去。
折腾了一阵,总结出两种可行的方案,大家可以根据自己的定制需求选:
方案1:手动定义Subcomponent(完全掌控)
如果需要高度定制,不如直接放弃@ContributesAndroidInjector的自动生成,手动写Subcomponent和对应的Factory,这样想怎么改就怎么改:
首先定义MainActivity的Subcomponent:
@PerActivity @Subcomponent(modules = [MainActivityModule::class]) interface MainActivitySubcomponent : AndroidInjector<MainActivity> { // 这里可以添加自定义方法,比如暴露某个依赖给外部 fun getSomeDependency(): SomeDependency // 自定义Factory,支持动态传参 @Subcomponent.Factory interface Factory : AndroidInjector.Factory<MainActivity> { // 除了默认的create方法,还可以加带参数的版本 override fun create(instance: MainActivity): MainActivitySubcomponent // 比如需要传userId的话: // fun create(instance: MainActivity, userId: String): MainActivitySubcomponent } }
然后在AppComponent里声明这个Subcomponent的Factory:
@Component(modules = [AndroidInjectionModule::class, ActivitiesModule::class, AppModule::class]) interface AppComponent { fun mainActivitySubcomponentFactory(): MainActivitySubcomponent.Factory }
接下来修改ActivitiesModule,把原来的@ContributesAndroidInjector换成绑定Subcomponent Factory的代码:
@Module abstract class ActivitiesModule { @Binds @IntoMap @ActivityKey(MainActivity::class) abstract fun bindMainActivityInjectorFactory(factory: MainActivitySubcomponent.Factory): AndroidInjector.Factory<out Activity> // 其他Activity的绑定同理 }
这样一来,你就完全掌控了Subcomponent的结构,不管是加自定义方法还是动态传参都没问题。如果需要动态传参,在Activity的onCreate里可以手动获取Factory并调用带参数的create方法:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { val userId = intent.getStringExtra("USER_ID") val appComponent = (application as MyApp).appComponent appComponent.mainActivitySubcomponentFactory() .create(this, userId) .inject(this) super.onCreate(savedInstanceState) } }
方案2:给@ContributesAndroidInjector加定制模块
如果只是需要添加一些额外的绑定,或者简单的动态参数,可以不用完全放弃简化API,而是给@ContributesAndroidInjector的modules参数加自定义模块,比如:
先写一个带参数的模块:
@Module class MainActivityModule(private val userId: String) { @Provides @PerActivity fun provideUserId(): String { return userId } }
然后在ActivitiesModule里还是用@ContributesAndroidInjector,但指定这个模块:
@Module abstract class ActivitiesModule { @PerActivity @ContributesAndroidInjector(modules = [MainActivityModule::class]) abstract fun contributeMainActivityInjector(): MainActivity }
这时候需要手动创建模块实例并传入参数,所以得在Activity里手动注入:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { val userId = intent.getStringExtra("USER_ID") val appComponent = (application as MyApp).appComponent // 这里需要用Dagger生成的Subcomponent Builder,名字一般是DaggerMainActivitySubcomponent DaggerMainActivitySubcomponent.builder() .appComponent(appComponent) .mainActivityModule(MainActivityModule(userId)) .create(this) .inject(this) super.onCreate(savedInstanceState) } }
这种方式保留了部分简化API的便利,同时能满足简单的定制需求。
内容的提问来源于stack exchange,提问作者Joao Sousa

