前言
Dagger2 基础分析了 @Inject,@Component,@Module,@Provides 是如何构成 dagger2 整个依赖注入框架的。
主线已经完成,dagger2 中剩下的 @Qualifier(限定符), @Singleton(单例),@Scope(作用域)是对整个依赖注入框架细节上完善,提供更完善的功能。
@Qualifier
Dagger2 基础中分析到 @Component 是一个注入器,起着桥梁的作用。被依赖类的实例有两种创建方式:
- @Inject 标注构造函数创建
- @Module 中工厂模式创建
这两种方式是有优先级之分的,Component会首先从Module中查找实例,找不到才会去查找Inject方式创建的实例。
问题随之而来,在同一种实例创建方式中,可能有多个方法创建类示例,注入器应该选择哪个? @Qualifier 的作用就是用来决定 Component 做选择的。
我们使用 @Qualifier 来定义自己的注解,然后通过自定义注解去标注依赖的方法和依赖需求方,这样 Dagger2 就知道为谁提供依赖了。
Component 组织方式
@Scope, @Singleton 有些坑,结合Component讲解,个人认为会更要效果。
如何划分Component
如果一个 app 只有一个Component,脑补一下吧,简直是世界末日。这个Component会很难维护,变化率极高,很庞大,职责不明确。所以我们要将Component进行划分
- 要有一个全局的Component,定义为 ApplicationComponent,负责管理整个 app 的全局类示例(整个app都要用到的类的实例,比如applicationContext,这些类都是单例的)
- 每一个页面对应一个Component,比如一个Activity或者Fragment页面定义一个Component,当然这个不是必须的,某些页面依赖的类是一样的,可以共用一个Component。
@Singleton仅起标注作用
前面提到 ApplicationComponent 负责管理整个app用到的全局类示例,如何才能创建单例?
- Module 中定义创建全局类实例
- ApplicationComponent 管理Module
- 保证ApplicationComponent只有一个实例(在app的Application中实例化)123456789public static AppComponent getAppComponent(){if (appComponent == null) {appComponent = DaggerAppComponent.builder().appModule(new AppModule(instance)).httpModule(new HttpModule()).build();}return appComponent;}
Dagger2 中真正创建单例的方法是上面的不走,全局类实例的生命周期就和Application一致了。疑问又来了,@Singleton 岂不是多余的? 答案当然是NO!!! @Singleton有以下作用:
- 更好的管理 ApplicationComponent 和 Module 之间的关系,保证 ApplicationComponent 和 Module是匹配的。若两者的作用域不一样,则在编译时报错。
- 提高代码可读性,让猿们清晰的看到 Module中创建的类实例是单例。
组织Component
我们划分好Component之后,全局类实例也已经创建了单例模式,如果其他的Component想要把全局的类实例注入到目标类中,怎么办? 我们需要组织Component之间的关系。 具体的组织方式有以下2种:
- 依赖方式
一个Component是依赖于一个或多个Component,使用dependencies
属性来添加依赖的Component - 包含方式
一个 Component 包含一个或多个Component,被包含的Component还可以继续包含其他的Component。 使用 @Subcomponent 注解组织Component
@Scope的真正用处
@Scope的真正用处是用于组织Component
- 为了更好的管理Component,不管是依赖还是包含,都有必要用自定义的Scope标注,使用不同的自定义Scope来提现Component之间的组织方式。编译器会检查有依赖关系或包含关系的Component,如果没有自定义Scope标注,会报错。
- 更好的管理Component与Module之间的匹配关系,编译器会检查Component管理的Modules,若发现标注Component的自定义Scope注解与Modules中的标注创建类示例方法的注解不一样,报错。
- 可读性提高,比如用Singleton标注全局类,这样我们可以立刻明白这类是全局单例类。
Dagger原理分析
Module
我们的代码
生成的工厂类代码
- ActivityModule_ProvideActivityFactory 中的 get() 方法调用了 ActivityModule 中的 provideActivity() 方法来获取我们需要的依赖 provideActivity 对象。
- ActivityModule_ProvideActivityFactory 的对象是由 create() 方法创建,并且传入 ActivityModule 对象。
那么一定有地方调用了 create() 方法创建 Factory 对象,并通过 get 方法获取实例。
之前多次说到 Component 是依赖提供方和依赖需求方之间的桥梁,下面我们分析一下,Dagger2 是如何通过 Component 将两者联系起来的。
Component
我们的代码
生成代码
- Dagger2 生成了 ActivityComponent 的实现类 DaggerActivityComponent
- DaggerActivityComponent 对象是在调用 build() 方法时进行初始化(initialize方法)
- 初始化Provider
- 初始化MembersInjector
- inject 方法中,调用injectMembers() 进行注入。
总结
关于 Dagger2 的的一些概念,就介绍到这里了。啰里啰嗦的说了好多,我们来总结一下 Dagger2 到底有些什么好处呢,应该如何使用。
好处
提高开发效率,不做油漆工
Dagger2 把 new 对象和注入的工作给做了,我们只需要将精力集中在关建业务上。
省去了单例的写法,也不用再操心单例线程安全的问题。更方便的管理类实例
每个app中的ApplicationComponent管理整个app的全局类实例,他们的生命周期和app的生命周期一样。
每个页面的Component管理自己页面所以来的类实例。
Component,Module让整个类实例结构变得很清晰。解耦
正常情况下,new 关键字到处都有,一旦类的构造函数发生变化,咱们哭死的心都有。。。设计模式中提倡把容易变化的部分封装起来。
Dagger2 通过 @Inject 注解 和 @Module 来创建实例
@Inject构造函数变化时,我们不用做任何修改
@Module 管理的实例,我们也只需要修改Module,比如HttpModule。
使用
- 依赖注入的流程
- 查找Module中是否存在创建该类实例的方法
- 存在创建类实例的方法,查看该方法是否需要参数
2.1 需要,则从步骤1开始,初始化每个参数
2.2 不需要,直接初始化类实例,一次依赖注入完成- 若不存在创建类方法,则查找Inject注解的构造函数,查看是否需要参数
3.1 需要,从步骤1开始一次初始化每个参数
3.2 不需要,初始化该实例,一次依赖注入完成
- 使用注意事项
- 一个app必须有一个Component来管理整个app的全局类实例
- 多个页面可以共享一个Component
- 最好使用自定义Scope,增强代码阅读性,及早的暴露问题(编译时检查)。