框架源码:https://github.com/google/dagger
Dagger 2 完全解析系列:
Dagger 2 完全解析(一),Dagger 2 的基本使用与原理
Dagger 2 完全解析(二),进阶使用 Lazy、Qualifier、Scope 等
Dagger 2 完全解析(三),Component 的组织关系与 SubComponent
Dagger 2 完全解析(四),Android 中使用 Dagger 2
Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
Dagger 2 完全解析(六),dagger.android 扩展库的使用
Dagger 2 完全解析(一),Dagger 2 的基本使用与原理
https://www.jianshu.com/p/26d9f99ea3bb
-
什么是依赖注入
依赖注入(Dependency Injection,简称 DI)是用于实现控制反转(Inversion of Control,缩写为 IoC)最常见的方式之一,控制反转是面向对象编程中的一种设计原则,用以降低计算机代码之间耦合度。控制反转的基本思想是:借助“第三方”实现具有依赖关系的对象之间的解耦。一开始是对象 A 对 对象 B 有个依赖,对象 A 主动地创建 对象 B,对象 A 有主动控制权,实现了 Ioc 后,对象 A 依赖于 Ioc 容器,对象 A 被动地接受容器提供的对象 B 实例,由主动变为被动,因此称为控制反转。注意,控制反转不等同于依赖注入,控制反转还有一种实现方式叫“依赖查找”(Denpendency Lookup)。更多控制反转的信息请看控制反转的维基百科。
依赖注入就是将对象实例传入到一个对象中去(Denpendency injection means giving an object its instance variables)。依赖注入是一种设计模式,降低了依赖和被依赖对象之间的耦合,方便扩展和单元测试。
依赖注入的实现方式
Dagger 2 的基本使用
- 引入 Dagger 2
查询版本号:https://search.maven.org/search?q=dagger
implementation 'com.google.dagger:dagger:2.21'
annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
implementation 'com.google.dagger:dagger-android:2.21'
implementation 'com.google.dagger:dagger-android-support:2.21'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.21'
如何在gradle中定义变量(比如 版本号)并引用?
- 使用 @Inject 标注需要注入的依赖
创建所依赖对象的实例
@Inject可以标注成员变量及构造函数
build 后可以在build/generated/source/apt目录下看到 Dagger 2 编译时生成的
- Component 桥梁
@Component可以标注接口或抽象类,Component 桥梁可以完成依赖注入过程,其中最重要的是定义注入接口,调用注入接口就可以完成 Man 所需依赖的注入。
build 后会生成带有Dagger前缀的实现该接口的类
Component 就是连接依赖的对象实例和需要注入的实例属性之间的桥梁。Component 会查找目标类对应的成员属性注入类(即 MembersInjector<Man>),然后把依赖属性的工厂实例(即 Car_Factory.create())传给注入类,再使用 Component 一开始定义的接口就能完成依赖注入。
注意,Component 中注入接口的参数必须为需要注入依赖的类型,不能是 Man 的父类或子类,注入接口返回值为 void,接口名可以任意。
接下来只需要在 Man 中调用injectMan方法就能完成注入。 - Module
使用@Inject标注构造函数来提供依赖的对象实例的方法,不是万能的,在以下几种场景中无法使用:
* 接口没有构造函数
* 第三方库的类不能被标注
* 构造函数中的参数必须配置
这时,就可以用@Provides标注的方法来提供依赖实例,方法的返回值就是依赖的对象实例,@Provides方法必须在Module中,Module 即用@Module标注的类。所以 Module 是提供依赖的对象实例的另一种方式。
约定俗成的是@Provides方法一般以provide为前缀,Moudle 类以Module为后缀,一个 Module 类中可以有多个@Provides方法。
接下来,需要把可以提供依赖实例的 Module 告诉 Component:
CarModule_ProvideCarFactory 和之前的 Car_Factory 类似,都实现 Factory<Car> 接口。
生成的 DaggerManComponent 和之前相比只改变了一个方法: - 总结
Dagger 2 最核心的三个部分
- 需要注入依赖的目标类,需要注入的实例属性由@Inject标注。
- 提供依赖对象实例的工厂,用@Inject标注构造函数或定义Module这两种方式都能提供依赖实例,Dagger 2 的注解处理器会在编译时生成相应的工厂类。
Module的优先级比@Inject标注构造函数的高
,意味着 Dagger 2 会先从 Module 寻找依赖实例。- 把依赖实例工厂创建的实例注入到目标类中的 Component。
下面再讲述上面提到的在 Dagger 2 种几个注解的用法:
- @Inject 一般情况下是标注成员属性和构造函数,
标注的成员属性不能是private
,Dagger 2 还支持方法注入,@Inject还可以标注方法
。@Provides 只能标注方法,必须在 Module 中
。- @Module 用来标注 Module 类
@Component 只能标注接口或抽象类
,声明的注入接口的参数类型必须和目标类一致。
完整代码:
https://github.com/WaterYuanData/AIDL/tree/master/teatdagger/src/main/java/com/yuan/teatdagger/dagger2
com.yuan.teatdagger.dagger2包下是@Inject构造函数
注入
com.yuan.teatdagger.dagger2.module包下是@Module类@Provides方法
注入
Dagger 2 完全解析(二),进阶使用 Lazy、Qualifier、Scope 等
-
Lazy (延迟注入)
有时我们想注入的依赖在使用时再完成初始化,加快加载速度,就可以使用注入Lazy<T>。只有在调用 Lazy<T> 的 get() 方法时才会初始化依赖实例注入依赖。 -
Provider 注入
有时候不仅仅是注入单个实例,我们需要多个实例,这时可以使用注入Provider<T>,每次调用它的 get() 方法都会调用到 @Inject 构造函数创建新实例或者 Module 的 provide 方法返回实例。 -
Qualifier(限定符)的作用相当于起了个区分的别名。
试想这样一种情况:沿用之前的 Man 和 Car 的例子,如果 CarModule 提供了两个生成 Car 实例的 provide 方法,Dagger 2 在注入 Car 实例到 Man 中时应该选择哪一个方法呢?
- 在 provide 方法上加上@Named注解,用来区分
- 还需要在 Inject 注入的地方加上@Named注解,表明需要注入的是哪一种 Car
Scope(作用域)
@Scope是元注解,是用来标注自定义注解的,MyScope 就是一个 Scope 注解
@Documented
@Retention(RUNTIME)
@Scope
public @interface MyScope {}
Scope 注解只能标注目标类、@provide 方法和 Component。Scope 注解要生效的话,需要同时标注在 Component 和提供依赖实例的 Module 或目标类上。Module 中 provide 方法中的 Scope 注解必须和 与之绑定的 Component 的 Scope 注解一样
,否则作用域不同会导致编译时会报错。
Scope 作用域的本质:Component 间接持有依赖实例的引用,把实例的作用域与 Component 绑定
,它们不是同年同月同日生,但是同年同月死。
-
Singleton Scope
Singleton 作用域可以保证一个 Component 中的单例,但是如果产生多个 Component 实例,那么实例的单例就无法保证了。
应用场景:AppComponent
Component 可以同时被多个 Scope 标记
-
Reusable Scope
只想单纯缓存依赖的实例,可以复用之前的实例,不想关心与之绑定是什么 Component
Reusable 作用域只需要标记目标类或 provide 方法,不用标记 Component
-
Releasable references(可释放引用)
使用 Scope 注解时,Component 会间接持有绑定的依赖实例的引用,也就是说实例在 Component 还存活时无法被回收。而在 Android 中,应该尽量减少内存占用,把没有使用的对象释放,这时可以用@CanReleaseReferences标记 Scope 注解。
然后在 Application 中注入ReleasableReferenceManager对象,在内存不足时调用releaseStrongReferences()方法把 Component 间接持有的强引用变为弱引用。
-
Binding Instances
Component 可以间接持有 Module 或 Inject 目标类构造函数提供的依赖实例,除了这两种方式,Component 还可以在创建 Component 的时候绑定依赖实例,用以注入。 - 答疑
- 例如 Component 有 ScopeA 和 ScopeB 标记,那么它可以和 ScopeA 的 ModuleA、ScopeB 的 ModuleB 绑定。所以准确的说应该是,Module 的 Scope 必须包含在 Component 的 Scope 中
- 一般一个 Activity 只需要一个 Component,但是一个 Component 可以绑定多个 Module
- 在 @BindingInstance 例子中,定义的是 Builder activity(Activity activity); ,所以后面只能注入 Activity 类型。
而之前 Module 构造函数绑定依赖实例中,参数为 HomeActivity,但是后面的 provide 方法返回的类型为 Activity,所以也只能提供 Activity 类型的依赖。- 在activity中注入了一个presenter,这个presenter在哪里被销毁的
4.1首先,presenter 被你注入到的 Activity 引用了,所以在 activity 未被销毁前是不会被回收的
4.2 如果你使用了 Scope 作用域的话,管理 presenter 的 Component 还会间接保留一份 presenter 的引用,所以 presenter 的生命周期还和 Component 绑定,如果 component 实例被 activity 保留的话,那么 component 和 presenter 的生命周期都和 activity 一样了。
4.3 但是如果使用的 AppComponent 注入的话,AppComponent 被 application 保留实例,那么 presenter 的生命周期就会和 application 绑定了。
一个整合了大量主流开源项目高度可配置化的 Android MVP 快速集成框架
https://github.com/JessYanCoding/MVPArms/blob/master/MVPArms.md
Dagger2神器入门(一)
https://www.jianshu.com/p/dce5382fec5d
Dagger2从入门到放弃再到恍然大悟
https://www.jianshu.com/p/39d1df6c877d
Android_Dagger2篇——从小白最易上手的角度 + 最新dagger.android
https://www.jianshu.com/p/22c397354997
Dragger2快速入门浅析
https://blog.csdn.net/Jet_Green/article/details/80996657
深入浅出Dagger2 : 从入门到爱不释手
https://www.jianshu.com/p/626b2087e2b1