依赖
-
什么是依赖?举个例子:当你写一个
Activity你需要把一些数据写入数据库,这时你一般会再写个 DatabaseMgr 来实现这个功能能。 这样你的这个Activity将依赖DatabaseMgr来实现读写数据库的功能,DatabaseMgr也就成为了Activity的依赖。 -
获取依赖的3种方式
- The class constructs the dependency it needs.
- Grab it from somewhere else. Some Android APIs, such as Context getters and getSystemService(), work this way.
- Have it supplied as a parameter.
The third option is dependency injection! With this approach you take the dependencies of a class and provide them rather than having the class instance obtain them itself.
-
既然依赖以参数形式注入,那么构建一个依赖链路很深的对象就特别麻烦,Dagger 为我们提的自动构建依赖。
-
我们需要通过各种注解告诉 Dagger 三种信息:
-
谁依赖谁?通过
@Component定义依赖图,Dagger 以这个为根节点,递归解析从它的依赖,它的依赖的依赖, 它依赖的依赖的依赖... 并生成代码来构建这些依赖。A @Component interface gives the information Dagger needs to generate the graph at compile-time. The parameter of the interface methods define what classes request injection. -
如何构建依赖?有2种方式:
-
通过
@Inject注解依赖的构造方法;class LoginViewModel @Inject constructor() { ... } -
在
@Module里实现依赖的构建,并用@Provider或者@Binds注解;@Module class LoginModule { @Provides fun getLocalData(): LocalData{ return LocalData() } }@Module abstract class StorageModule { @Binds abstract fun provideStorage(storage: SharedPreferencesStorage): Storage }@Binds其实本身并不构建依赖,只是在需要构建一个接口的实现时,告诉 Dagger 用特定的某个实现罢了;
-
-
如何管理依赖对象的生命周期? Use
@Scopeto scope a type to the Component's lifecycle. 如果这个依赖是通过@Provider创建的,那么要注解@Provider的方法,而不是这个依赖
-
@Component、@Module 和 @Subcomponent 之间的关系
-
Component是个接口或者抽象类,它的生命周期一般和Application对应,Dagger 会生成代码实现它,我们就可以用这个类来获取(或者注入)依赖,或者获取它的Subcomponent来获取(或者注入)依赖; -
Module是普通或者抽象类,用来给它所属的Component/Subcomponent提供特殊依赖的构建;Similar to Components, Dagger Modules tell Dagger how to provide instances of a certain type. Dependencies are defined using the @Provides and @Binds annotations. -
Subcomponent和Component的功能类似,只是它从属于Component,必须通过Component来使用它。 Dagger 在编译时会生成Subcomponent的实现并作为Component的实现类的内部类,因此Subcomponent可以获取到Component所提供其他依赖; -
SubComponent不能直接挂在Component上, 需要定义个Module承载它,然后再把Module挂到Component上:@Subcomponent interface LoginComponent { ... } @Module(subcomponents = LoginComponent::class) class SubcomponentsModule { ... } @Component(modules = [SubcomponentsModule::class]) interface ApplicationComponent { ... } -
Module一般在同一个 Component 里面只定义一次;Good practice dictates that modules should only be declared once in a component (outside of specific advanced Dagger use cases).
Dagger 生成代码
dagger_generated_code.png
-
Dagger 为
Component生成Dagger$Component类,该类实例通过静态方法 create 以Builder的方式创建; -
Dagger$Component的成员变量有2类:- 被
@Module注解的类, 因为这种类定义了如何获取依赖,所以当然得持有它; -
Provider, 用来生产依赖,如果依赖生命周期和当前Component相同, 就用DoubleCheck做缓存,这样每次就不会生产新的依赖;
- 被
-
Dagger$Component有3种内部类-
Subcomponent的实现类,内部类可以访问外部类的成员变量,因此Subcomponent可以获取到所有Component可以获取到的依赖。 -
其他2种分别是用以创建自己的
Factory工厂类或者Builder类,这2种方式都是用来创建Dagger$Component自身,因此没有必要2者都同时存在,有了Factory就不会有Builder。Factory和Builder可以通过@Component.Factory和@Component.Builder来显示定义。对于Component一般可以不用显示定义它的Factory或者Builder,但SubComponent一般都需要显示定义它的Factory; 原因如下:- 因为使用者不能直接使用
Subcomponent,使用者必须通过Dagger$Component来获取它的实例,而Dagger$Component获取Subcomponent的实例也是通过Subcomponent的Factory(或Builder),如果Subcomponent没有显示定义,Component不清楚隐式的(也就是将要生成的默认的)Factory或Builder具体是什么样的,也就不好生成对应的代码。(其实我认为 Dagger 应该是要知道的,因为代码都是它自己生成的) - 使用者要通过
Component获取Subcomponent的实例,就需要在Component里定义好获取Subcomponent的实例接口或者生成Subcomponent的Factory(Builder), 对于前者,缺乏可扩展性,所以 Dagger 选择了后者。 但Subcomponent的Factory和Builder将会是什么样的? 一种情况是隐式生成的,这就要在生成完隐式的Factory代码然后把它定义在 Component 里面,但 Subcomponent 代码生成又依赖Component的代码生成,代码生成过程会出现互相依赖的死循环。另外一种情况是显式定义的Factory, 这样就可以避免死循环依赖了,所以需要显示定义Subcomponent的Factory或者Builder。
- 因为使用者不能直接使用
-
-
Subcomponent的生成代码,将和Component生成的代码类似,这样就可以实现模块之间的嵌套;
Dagger For Android
-
在 app 中,依赖根节点一般是
Application、Activity和Fragment,比如在Activity 里面有个依赖需要注入,而这些对象是在 Framework 层构建的,所以这些对象的依赖需要手动调用 Dagger 来实现,比如在Application#onCreate,Activity#onCreate,Fragment#onAttach;在 Activity 或者 Fragment 比较多的情况下,会重复很多类似下面这样的代码片段:
(application as MyApplication).appComponent.loginComponent().create().inject(this)这样的代码有几个问题:
- 重复
- 依赖使用者需要知道依赖是怎么注入的,通过 applicaiton 转换为 MyApplication 在拿到 loginComponent 的 Factory 创建一个 Subcomponent,然后调用它的 inject 方法。 这样一系列的操作,对依赖使用者来说太不透明了。
那有没有办法定义一个 BaseActivity 或者 BaseFragment,在这里执行一次注入,并且注入过程对于使用者来说是透明的呢?那就要用到
AndroidInjector,先说一下使用方式,然后再解释一下原理:
使用方法
总共有 4 个步骤
-
Application类实现HasAndroidInjector接口:class MyApplication: Application(), HasAndroidInjector{ @Inject lateinit var dispatchingAndroidInjector: DispatchingAndroidInjector<Any> override fun androidInjector(): AndroidInjector<Any> { return dispatchingAndroidInjector; } override fun onCreate() { super.onCreate() DaggerApplicationComponent.create().inject(this) } } -
在
Componentinclude 内置的AndroidInjectionModule:@Component(modules = [AndroidInjectionModule::class, ...]) interface ApplicationComponent { ... } -
给
Activity定义一个 Subcomponent:
@Subcomponent
interface LoginComponent2: AndroidInjector<LoginActivity> {
@Subcomponent.Factory
interface Factory: AndroidInjector.Factory<LoginActivity> {}
}
@Module(subcomponents = [LoginComponent2::class])
abstract class LoginModule2{
@Binds
@IntoMap
@ClassKey(LoginActivity::class)
abstract fun provideLoginComponent2Factory(f: LoginComponent2.Factory): AndroidInjector.Factory<*>
}
-
把
LoginModule2include 到Component中后,就可以在BaseActivity中注入它的子类了:abstract class BaseActivity: Activity() { override fun onCreate(savedInstanceState: Bundle?) { AndroidInjection.inject(this) super.onCreate(savedInstanceState) } }以后新加的 Activity ,只要它继承
BaseActivity, 并定义好它自己的Subcomponent和Module,就可以实现依赖的自动注入了;
第 3 步骤,给每个 Activity 定义 Subcomponent 和 Module 还是比较繁琐的,用 @ContributesAndroidInjector 就可以让 Dagger 自动为我们生成 Subcomponent 和 Module:
@Module
abstract class ActivityModule {
@ContributesAndroidInjector
abstract fun loginActivity(): LoginActivity
}
Dagger 会生成如下代码(基本和手动写的差不多)
@Module(subcomponents = ActivityModule_LoginActivity.LoginActivitySubcomponent.class)
public abstract class ActivityModule_LoginActivity {
private ActivityModule_LoginActivity() {}
@Binds
@IntoMap
@ClassKey(LoginActivity.class)
abstract AndroidInjector.Factory<?> bindAndroidInjectorFactory(
LoginActivitySubcomponent.Factory builder);
@Subcomponent
public interface LoginActivitySubcomponent extends AndroidInjector<LoginActivity> {
@Subcomponent.Factory
interface Factory extends AndroidInjector.Factory<LoginActivity> {}
}
}
原理
-
通过
@IntoMap@ClassKey注解,告诉 Dagger 用LoginComponent2.Factory这个 Factory 生成的Subcomponent来注入LoginActivity。 这样生成的Dagger$Component代码会建立一个 Activity 对应 Provider 的 map:private Map<Class<?>, Provider<AndroidInjector.Factory<?>>> getMapOfClassOfAndProviderOfAndroidInjectorFactoryOf() { return Collections.<Class<?>, Provider<AndroidInjector.Factory<?>>>singletonMap(LoginActivity.class, (Provider) loginActivitySubcomponentFactoryProvider); } -
这个 map 会被传入
DispatchingAndroidInjector里面:public final class DispatchingAndroidInjector<T> implements AndroidInjector<T> { ... private final Map<String, Provider<AndroidInjector.Factory<?>>> injectorFactories; ... } -
然后
AndroidInjector.inject其实就是通过DispatchingAndroidInjector.inject来实现注入的:public boolean maybeInject(T instance) { Provider<AndroidInjector.Factory<?>> factoryProvider = injectorFactories.get(instance.getClass().getName()); AndroidInjector.Factory<T> factory = (AndroidInjector.Factory<T>) factoryProvider.get(); ... AndroidInjector<T> injector = checkNotNull( factory.create(instance), "%s.create(I) should not return null.", factory.getClass()); injector.inject(instance); }
使用过程注意点
-
dagger、dagger-android-support 等各个插件版本要保持一致:
implementation 'com.google.dagger:dagger:2.27' kapt 'com.google.dagger:dagger-compiler:2.27' annotationProcessor 'com.google.dagger:dagger-compiler:2.27' implementation 'com.google.dagger:dagger-android-support:2.27' kapt "com.google.dagger:dagger-android-processor:2.27"
Disadvantages of Dagger
- 学习成本高;
- 进一步限定了app的开发模式;
- 需要暴露依赖成员变量的访问属性;
- 阅读代码不方便,依赖和使用者隔离,逻辑不直观连贯;








网友评论