[TOC]
推荐两篇Dagger2学习教程:[《Dagger2 入门,以初学者角度》](https://www.jianshu.com/p/1d84ba23f4d2)和[《Android:dagger2让你爱不释手-基础依赖注入框架篇》](https://www.jianshu.com/p/cd2c1c9f68d4)
# 学习总结
## 总览
1、Java 中万物皆对象,每一个对象可以拥有很多成员变量,这些成员变量可以是各种对象
2、依赖注入,顾名思义指一个类所依赖的对象的注入过程,省去了手动去为一个对象 new 一堆依赖的过程
3、Module 是依赖提供方,Activity、Fragment 等是依赖需求方,Component 是依赖注入器
4、Component 是一个接口,编译代码后,会自动生成一个实现类
## 工作流程
1、调用 Component 实现类的 inject 方法,把依赖需求方对象(比如 Activity 自身),传递给 Component
2、Component 根据依赖需求方的需求,从 Module 中寻找现成的实例对象,并注入给依赖需求方(Activity)
## 关键注解说明
### @Component
一个标注的 @Component 注解形式如下:
```java
@Component(dependencies = AppComponent.class,modules = {MainModule.class, ActivityModule.class})
public interface MainComponent extends ActivityComponent{
...
}
```
@Component 注解有两个属性:dependencies 和 modules。dependencies 属性声明 @Component 的依赖关系,modules 属性声明 @Component 后期会从哪些 @Module 中去寻找依赖实例。
我们在 创建 @Component 的实现类对象时,需要提供父 Component 实例对象和 Module 实例对象,如下:
```java
mMainComponent = DaggerMainComponent.builder().appComponent(getAppComponent())
.mainModule(new MainModule())
.activityModule(new ActivityModule(this)).build();
mMainComponent.inject(this);
```
### @Scope
Scope 的意思是“范围”,即用来声明依赖对象作用范围的
> Dagger2 有一个机制:在同一个作用范围内,@Provides 方法提供的依赖对象会变成单例。即依赖需求方不管依赖几次 @Provides 提供的对象,Dagger2 都会只调用一次该 @Provides 方法来获取依赖实例,后期再需要该依赖实例时直接进行提供。
@Scope 注解需要自定义注解才可以使用,@Singleton 就是一个自定义注解:
```java
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
```
依赖提供方 Module 的 @Provides 方法使用 @Singleton 注解声明,依赖注入器 Component 也使用 @Singleton 注解声明即可。这样 Module 提供的依赖对象就是单例的了。
### @Qulifier
Dagger2 是通过返回值类型来从 Module 中获取依赖对象的。也就是说像如下的 Module 代码中,Component 并不知道该调用哪一个方法来生成对象:
```java
@Provides
public Cloth getRedCloth() {
Cloth cloth = new Cloth();
cloth.setColor("红色");
return cloth;
}
@Provides
public Cloth getBlueCloth() {
Cloth cloth = new Cloth();
cloth.setColor("蓝色");
return cloth;
}
```
所以需要一个 Qulifier 注解来进行区分:
```java
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface RedCloth {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BlueCloth {
}
```
使用 @RedCloth 和 @BlueCloth 对依赖需求方和依赖提供方进行注解,便可进行区分
# 源码分析
## 注解
Dagger2 的工作主要是通过注解来实现的,所以理解注解至关重要,这里推荐一篇讲解注解很清晰的文章:[秒懂,Java 注解 (Annotation)你可以这样学](https://blog.csdn.net/briblue/article/details/73824058)
## 注解处理器
和 EventBus、ButterKnife 一样,Dagger2 也是通过注解处理器来在编译器进行代码生成,关于注解处理器可以参考我的另一篇博文:[EventBus 3.0 源码分析(二)注解处理器的使用](http://xuchongyang.com/2017/07/17/EventBus-3-0-%E6%BA%90%E7%A0%81%E5%88%86%E6%9E%90%EF%BC%88%E4%BA%8C%EF%BC%89%E7%BC%96%E8%AF%91%E6%97%B6%E6%B3%A8%E8%A7%A3%E5%92%8C%E8%BF%90%E8%A1%8C%E6%97%B6%E6%B3%A8%E8%A7%A3/)。
## 源码分析
我们先来定义一个最简单的 Dagger2 的使用实例:
MainActivity 类如下:
```java
public class MainActivity extends AppCompatActivity {
@Inject
public Computer mComputer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MainComponent mainComponent = DaggerMainComponent.builder()
.mainModule(new MainModule())
.build();
mainComponent.inject(this);
}
}
```
@Module 类如下:
```java
@Module
public class MainModule {
@Provides
public Computer providesAppleComputer() {
return new Computer("Apple");
}
}
```
@Component 类如下:
```java
@Component(modules = MainModule.class)
public interface MainComponent {
void inject(MainActivity activity);
}
```
然后点击 rebuild project 进行编译。我这边在第一次进行代码编译时,并没有如预期生成 @Component 的实现类,发现由于 Dagger2 的引入是在 子 Module 中引入的,因此在 app 模块中注解处理器无法为我们生成代码。只要在主模块引入注解处理器即可:
```plain
annotationProcessor 'com.google.dagger:dagger-compiler:2.15'
```
在 MainActivity 代码中可以看到,我们先通过 Component 的实现类的 builder 模式获取到实例对象,再调用 inject 方法进行依赖注入。注入成功后此时 MainActivity 的成员变量就已经赋值了。
我们看看 Component 的实现类 DaggerMainComponent 的源码:
```java
public final class DaggerMainComponent implements MainComponent {
private MainModule mainModule;
private DaggerMainComponent(Builder builder) {
initialize(builder);
}
public static Builder builder() {
return new Builder();
}
public static MainComponent create() {
return new Builder().build();
}
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainModule = builder.mainModule;
}
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectMComputer(
instance, MainModule_ProvidesAppleComputerFactory.proxyProvidesAppleComputer(mainModule));
return instance;
}
public static final class Builder {
private MainModule mainModule;
private Builder() {}
public MainComponent build() {
if (mainModule == null) {
this.mainModule = new MainModule();
}
return new DaggerMainComponent(this);
}
public Builder mainModule(MainModule mainModule) {
this.mainModule = Preconditions.checkNotNull(mainModule);
return this;
}
}
}
```
代码精简一下,其实也很简单,仅仅是一个标准的 builder 模式,外加实现了 Component 接口的 inject 方法:
```java
public final class DaggerMainComponent implements MainComponent {
private MainModule mainModule;
public static Builder builder() {
return new Builder();
}
// ...
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
public static final class Builder {
// ...
}
}
```
接下来我们一一看:
**1、Component使用Builder模式创建Module成员变量**
首先是 builder 模式的 build 方法:
```java
public MainComponent build() {
if (mainModule == null) {
this.mainModule = new MainModule();
}
return new DaggerMainComponent(this);
}
```
```java
private DaggerMainComponent(Builder builder) {
initialize(builder);
}
```
```java
@SuppressWarnings("unchecked")
private void initialize(final Builder builder) {
this.mainModule = builder.mainModule;
}
```
很好理解,其实就是给 DaggerMainComponent 的成员变量 mainModule 对象进行赋值操作。接下来看看比较关键的注入操作:
**2、Component为待注入对象进行依赖注入**
```java
@Override
public void inject(MainActivity activity) {
injectMainActivity(activity);
}
```
```java
private MainActivity injectMainActivity(MainActivity instance) {
MainActivity_MembersInjector.injectMComputer(
instance, MainModule_ProvidesAppleComputerFactory.proxyProvidesAppleComputer(mainModule));
return instance;
}
```
可以看到,注入操作是调用了 MainActivity_MembersInjector 这个类的 injectMComputer 方法,看看这个方法:
```java
// MainActivity_MembersInjector.java
public static void injectMComputer(MainActivity instance, Computer mComputer) {
instance.mComputer = mComputer;
}
```
在 injectMComputer 方法中,直接把第二个参数 Computer 对象赋值给依赖需求方 MainActivity。MainActivity 是我们直接手动传递过来的,那么第二个参数的依赖对象是怎么来的呢,也就是 `MainModule_ProvidesAppleComputerFactory.proxyProvidesAppleComputer(mainModule)` 这一行代码是如何拿到一个目标依赖对象的呢?
编译时 Dagger2 自动为我们生成了 MainModule_ProvidesAppleComputerFactory 这个类,看名称就知道是提供 AppleComputer 的工厂类,对应着我们 MainModule 中的 providesAppleComputer。看看 proxyProvidesAppleComputer 方法:
```java
// MainModule_ProvidesAppleComputerFactory.java
public static Computer proxyProvidesAppleComputer(MainModule instance) {
// 此处做一下非空判断
return Preconditions.checkNotNull(
instance.providesAppleComputer(),
"Cannot return null from a non-@Nullable @Provides method");
}
```
参数 MainModule 就是刚才在 builder 方法中初始化的那个 MainModule。最终返回的是 MainModule 的 providesAppleComputer 方法所提供的实例对象。至此,Component 就完成了从 Module 中获取依赖实例对象,并注入到依赖需求方的过程。
## 总结
1、Component的实现类采用Builder模式,完成Component成员变量Module的设置。
2、Component实现类的inject方法,参数为等待注入的对象(如Activity等),将Module提供的对象设置给等待注入的对象。
- 导读
- Java知识
- Java基本程序设计结构
- 【基础知识】Java基础
- 【源码分析】Okio
- 【源码分析】深入理解i++和++i
- 【专题分析】JVM与GC
- 【面试清单】Java基本程序设计结构
- 对象与类
- 【基础知识】对象与类
- 【专题分析】Java类加载过程
- 【面试清单】对象与类
- 泛型
- 【基础知识】泛型
- 【面试清单】泛型
- 集合
- 【基础知识】集合
- 【源码分析】SparseArray
- 【面试清单】集合
- 多线程
- 【基础知识】多线程
- 【源码分析】ThreadPoolExecutor源码分析
- 【专题分析】volatile关键字
- 【面试清单】多线程
- Java新特性
- 【专题分析】Lambda表达式
- 【专题分析】注解
- 【面试清单】Java新特性
- Effective Java笔记
- Android知识
- Activity
- 【基础知识】Activity
- 【专题分析】运行时权限
- 【专题分析】使用Intent打开三方应用
- 【源码分析】Activity的工作过程
- 【面试清单】Activity
- 架构组件
- 【专题分析】MVC、MVP与MVVM
- 【专题分析】数据绑定
- 【面试清单】架构组件
- 界面
- 【专题分析】自定义View
- 【专题分析】ImageView的ScaleType属性
- 【专题分析】ConstraintLayout 使用
- 【专题分析】搞懂点九图
- 【专题分析】Adapter
- 【源码分析】LayoutInflater
- 【源码分析】ViewStub
- 【源码分析】View三大流程
- 【源码分析】触摸事件分发机制
- 【源码分析】按键事件分发机制
- 【源码分析】Android窗口机制
- 【面试清单】界面
- 动画和过渡
- 【基础知识】动画和过渡
- 【面试清单】动画和过渡
- 图片和图形
- 【专题分析】图片加载
- 【面试清单】图片和图形
- 后台任务
- 应用数据和文件
- 基于网络的内容
- 多线程与多进程
- 【基础知识】多线程与多进程
- 【源码分析】Handler
- 【源码分析】AsyncTask
- 【专题分析】Service
- 【源码分析】Parcelable
- 【专题分析】Binder
- 【源码分析】Messenger
- 【面试清单】多线程与多进程
- 应用优化
- 【专题分析】布局优化
- 【专题分析】绘制优化
- 【专题分析】内存优化
- 【专题分析】启动优化
- 【专题分析】电池优化
- 【专题分析】包大小优化
- 【面试清单】应用优化
- Android新特性
- 【专题分析】状态栏、ActionBar和导航栏
- 【专题分析】应用图标、通知栏适配
- 【专题分析】Android新版本重要变更
- 【专题分析】唯一标识符的最佳做法
- 开源库源码分析
- 【源码分析】BaseRecyclerViewAdapterHelper
- 【源码分析】ButterKnife
- 【源码分析】Dagger2
- 【源码分析】EventBus3(一)
- 【源码分析】EventBus3(二)
- 【源码分析】Glide
- 【源码分析】OkHttp
- 【源码分析】Retrofit
- 其他知识
- Flutter
- 原生开发与跨平台开发
- 整体归纳
- 状态及状态管理
- 零碎知识点
- 添加Flutter到现有应用
- Git知识
- Git命令
- .gitignore文件
- 设计模式
- 创建型模式
- 结构型模式
- 行为型模式
- RxJava
- 基础
- Linux知识
- 环境变量
- Linux命令
- ADB命令
- 算法
- 常见数据结构及实现
- 数组
- 排序算法
- 链表
- 二叉树
- 栈和队列
- 算法时间复杂度
- 常见算法思想
- 其他技术
- 正则表达式
- 编码格式
- HTTP与HTTPS
- 【面试清单】其他知识
- 开发归纳
- Android零碎问题
- 其他零碎问题
- 开发思路