受限于Android结构的特殊性,当我们在Android项目中使用Dagger时,不得不在组件的生命周期中编写一些模板代码:
class FrombulationActivity : Activity() {
@Inject
lateinit var frombulator: Frombulator
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
(contxt.applicationContext as MyApplication)
.getApplicationComponent()
.newActivityComponentBuilder()
.activity(this)
.build()
.inject(this)
}
}
为了实现对Android组件依赖项的注入,我们不得不在每一个需要的组件的生命周期中复制-粘贴这些模板代码,这是一种”坏味道“。另外,更重要的是,发起请求注入的客户端需要知道它的注入器,这破坏了依赖注入的核心原则:一个类不应知道如何实现依赖注入。
为了解决这个问题,google提供了dagger的安卓特供版:dagger-android。
依赖引入
apply plugin: 'kotlin-kapt'
dependencies {
implementation 'com.google.dagger:dagger-android:2.x'
// 如果需要使用support库或androidx中的组件
implementation 'com.google.dagger:dagger-android-support:2.x'
kapt 'com.google.dagger:dagger-compiler:2.x'
kapt 'com.google.dagger:dagger-android-processor:2.x'
}
为Activity注入
-
将
AndroidInjectionModule注册到Application Component中。 -
为请求注入的Android组件提供一个SubComponent,该SubComponent继承自
AndroidInjector<T>,并且该SubComponent拥有一个继承自AndroidInjector.Factory<T>的Factory接口:@Subcomponent interface MainComponent : AndroidInjector<MainActivity> { @Subcomponent.Factory interface Factory : AndroidInjector.Factory<MainActivity> } -
为SubComponent编写一个用于绑定子组件的Module,并在其中提供一个用于绑定SubComponent的Factory的方法:
@Module(subcomponents = [MainComponent::class]) abstract class SubComponentsModule { @Binds @IntoMap @ClassKey(MainActivity::class) abstract fun bindMainActivityInjectorFactory( factory: MainComponent.Factory ): AndroidInjector.Factory<*> }如果子组件和其工厂中除了步骤2、步骤3中提到的内容以外,没有其他方法和supertype时(其实大部分情况下你都不需要编写额外的内容,因为
Factory限制了create方法的定义而SubComponentsModule不允许提供任何其它的绑定),你可以使用@ContributesAndroidInjector注解来简化上述的模板代码:@Module abstract class SubComponentsModule { @YourScope @ContributesAndroidInjector(modules = [MainModule::class]) abstract fun contributeMainActivityInjector(): MainActivity } -
然后在Application Component中注册SubComponent。
@Component( modules = [ AndroidInjectionModule::class , SubComponentsModule::class ... ], ) interface AppComponent { ... } -
接着为Application实现
HasAndroidInjector接口,并且为其提供一个DispatchingAndroidInjector<Any>成员,该成员通过@Inject注入,并同时作为androidInjector()的返回值返回。class MyApplication : Application(), HasAndroidInjector { @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> = dispatchingAndroidInjector ... } -
最后,在Activity的
onCreate方法中调用AndroidInjection.inject(this)方法完成注入。
实现原理
为了弄清楚dagger-android的注入原理,现在我们从注入开始倒着分析源码。
首先是在Activity中的AndroidInjection.inject(this)
public static void inject(Activity activity) {
Application application = activity.getApplication();
...
inject(activity, (HasAndroidInjector) application);
}
private static void inject(Object target, HasAndroidInjector hasAndroidInjector) {
AndroidInjector<Object> androidInjector = hasAndroidInjector.androidInjector();
androidInjector.inject(target);
}
可以看到,AndroidInjection首先通过activity获取到application对象,然后调用其androidInjector()方法获取AndroidInjector实例。这个androidInjector()方法来自于HasAndroidInjector接口,我们之前已经在Application上实现了它,该方法会返回一个DispatchingAndroidInjector实例。
@Inject
lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any>
override fun androidInjector(): AndroidInjector<Any> = dispatchingAndroidInjector
随后AndroidInjection会把对activity的注入委派给DispatchingAndroidInjector实例。接下来我们看看DispatchingAndroidInjector中的注入。
@Override
public void inject(T instance) {
boolean wasInjected = maybeInject(instance);
if (!wasInjected) {
throw new IllegalArgumentException(errorMessageSuggestions(instance));
}
}
public boolean maybeInject(T instance) {
Provider<AndroidInjector.Factory<?>> factoryProvider =
injectorFactories.get(instance.getClass().getName());
if (factoryProvider == null) return false;
AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get();
try {
AndroidInjector<T> injector = factory.create(instance);
injector.inject(instance);
return true;
} catch (ClassCastException e) {
...
}
}
在DispatchingAndroidInjector中,首先会根据注入目标instance的类名(也就是具体的Activity名称)在injectorFactories中查找对应的Component工厂。如果找到了工厂类,就创建对应的Component,再通过component实例执行注入。
那么injectorFactories中保存的Component工厂是从哪里来的呢?
没错正是我们在SubComponentsModule中使用MultiBind提供的,通过代码可以看到Activity类对象和Component factory的映射通过DispatchingAndroidInjector的构造函数注入,而DispatchingAndroidInjector又是在Application中注入的,AppComponent中又依赖SubComponentsModule,至此整个注入过程顺利闭环。
private final Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactories;
@Inject
DispatchingAndroidInjector(
Map<Class<?>, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithClassKeys,
Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactoriesWithStringKeys
) {
this.injectorFactories = merge(injectorFactoriesWithClassKeys, injectorFactoriesWithStringKeys);
}
为Fragment注入
Fragment的注入与Activity的注入过程基本一致,唯一需要注意的是要在Fragment的onAttach生命周期回调中使用AndroidInjection**.**inject(this);。
另外dagger-android为Fragment提供了更加灵活的注入方式,Dagger允许我们将Fragment的Component定义为Application Component的SubComponent,或是承载它的Activity的Component的SubComponent,甚至是Fragment Component的SubComponent。定义好SubComponent的位置后,在父Component对应的组件上实现HasAndroidInjector接口即可。
框架基类
因为dagger-android是在运行时通过类名查找对应组件的注入器(Component),所以我们可以在基类中调用AndroidInjection.inject()来完成注入工作,或者实现HasAndroidInjector接口以实现对子模块的注入。
dagger-android也预置了一些已经实现好的基类,如DaggerApplication、DaggerActivity、DaggerFragment等等,以帮助我们快速开发。