[TOC]
推薦兩篇Dagger2學(xué)習(xí)教程:[《Dagger2 入門,以初學(xué)者角度》](https://www.jianshu.com/p/1d84ba23f4d2)和[《Android:dagger2讓你愛不釋手-基礎(chǔ)依賴注入框架篇》](https://www.jianshu.com/p/cd2c1c9f68d4)
# 學(xué)習(xí)總結(jié)
## 總覽
1、Java 中萬物皆對(duì)象,每一個(gè)對(duì)象可以擁有很多成員變量,這些成員變量可以是各種對(duì)象
2、依賴注入,顧名思義指一個(gè)類所依賴的對(duì)象的注入過程,省去了手動(dòng)去為一個(gè)對(duì)象 new 一堆依賴的過程
3、Module 是依賴提供方,Activity、Fragment 等是依賴需求方,Component 是依賴注入器
4、Component 是一個(gè)接口,編譯代碼后,會(huì)自動(dòng)生成一個(gè)實(shí)現(xiàn)類
## 工作流程
1、調(diào)用 Component 實(shí)現(xiàn)類的 inject 方法,把依賴需求方對(duì)象(比如 Activity 自身),傳遞給 Component
2、Component 根據(jù)依賴需求方的需求,從 Module 中尋找現(xiàn)成的實(shí)例對(duì)象,并注入給依賴需求方(Activity)
## 關(guān)鍵注解說明
### @Component
一個(gè)標(biāo)注的 @Component 注解形式如下:
```java
@Component(dependencies = AppComponent.class,modules = {MainModule.class, ActivityModule.class})
public interface MainComponent extends ActivityComponent{
...
}
```
@Component 注解有兩個(gè)屬性:dependencies 和 modules。dependencies 屬性聲明 @Component 的依賴關(guān)系,modules 屬性聲明 @Component 后期會(huì)從哪些 @Module 中去尋找依賴實(shí)例。
我們?cè)?創(chuàng)建 @Component 的實(shí)現(xiàn)類對(duì)象時(shí),需要提供父 Component 實(shí)例對(duì)象和 Module 實(shí)例對(duì)象,如下:
```java
mMainComponent = DaggerMainComponent.builder().appComponent(getAppComponent())
.mainModule(new MainModule())
.activityModule(new ActivityModule(this)).build();
mMainComponent.inject(this);
```
### @Scope
Scope 的意思是“范圍”,即用來聲明依賴對(duì)象作用范圍的
> Dagger2 有一個(gè)機(jī)制:在同一個(gè)作用范圍內(nèi),@Provides 方法提供的依賴對(duì)象會(huì)變成單例。即依賴需求方不管依賴幾次 @Provides 提供的對(duì)象,Dagger2 都會(huì)只調(diào)用一次該 @Provides 方法來獲取依賴實(shí)例,后期再需要該依賴實(shí)例時(shí)直接進(jìn)行提供。
@Scope 注解需要自定義注解才可以使用,@Singleton 就是一個(gè)自定義注解:
```java
@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton {}
```
依賴提供方 Module 的 @Provides 方法使用 @Singleton 注解聲明,依賴注入器 Component 也使用 @Singleton 注解聲明即可。這樣 Module 提供的依賴對(duì)象就是單例的了。
### @Qulifier
Dagger2 是通過返回值類型來從 Module 中獲取依賴對(duì)象的。也就是說像如下的 Module 代碼中,Component 并不知道該調(diào)用哪一個(gè)方法來生成對(duì)象:
```java
@Provides
public Cloth getRedCloth() {
Cloth cloth = new Cloth();
cloth.setColor("紅色");
return cloth;
}
@Provides
public Cloth getBlueCloth() {
Cloth cloth = new Cloth();
cloth.setColor("藍(lán)色");
return cloth;
}
```
所以需要一個(gè) Qulifier 注解來進(jìn)行區(qū)分:
```java
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface RedCloth {
}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface BlueCloth {
}
```
使用 @RedCloth 和 @BlueCloth 對(duì)依賴需求方和依賴提供方進(jìn)行注解,便可進(jìn)行區(qū)分
# 源碼分析
## 注解
Dagger2 的工作主要是通過注解來實(shí)現(xiàn)的,所以理解注解至關(guān)重要,這里推薦一篇講解注解很清晰的文章:[秒懂,Java 注解 (Annotation)你可以這樣學(xué)](https://blog.csdn.net/briblue/article/details/73824058)
## 注解處理器
和 EventBus、ButterKnife 一樣,Dagger2 也是通過注解處理器來在編譯器進(jìn)行代碼生成,關(guān)于注解處理器可以參考我的另一篇博文:[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/)。
## 源碼分析
我們先來定義一個(gè)最簡(jiǎn)單的 Dagger2 的使用實(shí)例:
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);
}
```
然后點(diǎn)擊 rebuild project 進(jìn)行編譯。我這邊在第一次進(jìn)行代碼編譯時(shí),并沒有如預(yù)期生成 @Component 的實(shí)現(xiàn)類,發(fā)現(xiàn)由于 Dagger2 的引入是在 子 Module 中引入的,因此在 app 模塊中注解處理器無法為我們生成代碼。只要在主模塊引入注解處理器即可:
```plain
annotationProcessor 'com.google.dagger:dagger-compiler:2.15'
```
在 MainActivity 代碼中可以看到,我們先通過 Component 的實(shí)現(xiàn)類的 builder 模式獲取到實(shí)例對(duì)象,再調(diào)用 inject 方法進(jìn)行依賴注入。注入成功后此時(shí) MainActivity 的成員變量就已經(jīng)賦值了。
我們看看 Component 的實(shí)現(xiàn)類 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;
}
}
}
```
代碼精簡(jiǎn)一下,其實(shí)也很簡(jiǎn)單,僅僅是一個(gè)標(biāo)準(zhǔn)的 builder 模式,外加實(shí)現(xiàn)了 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模式創(chuàng)建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;
}
```
很好理解,其實(shí)就是給 DaggerMainComponent 的成員變量 mainModule 對(duì)象進(jìn)行賦值操作。接下來看看比較關(guān)鍵的注入操作:
**2、Component為待注入對(duì)象進(jìn)行依賴注入**
```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;
}
```
可以看到,注入操作是調(diào)用了 MainActivity_MembersInjector 這個(gè)類的 injectMComputer 方法,看看這個(gè)方法:
```java
// MainActivity_MembersInjector.java
public static void injectMComputer(MainActivity instance, Computer mComputer) {
instance.mComputer = mComputer;
}
```
在 injectMComputer 方法中,直接把第二個(gè)參數(shù) Computer 對(duì)象賦值給依賴需求方 MainActivity。MainActivity 是我們直接手動(dòng)傳遞過來的,那么第二個(gè)參數(shù)的依賴對(duì)象是怎么來的呢,也就是 `MainModule_ProvidesAppleComputerFactory.proxyProvidesAppleComputer(mainModule)` 這一行代碼是如何拿到一個(gè)目標(biāo)依賴對(duì)象的呢?
編譯時(shí) Dagger2 自動(dòng)為我們生成了 MainModule_ProvidesAppleComputerFactory 這個(gè)類,看名稱就知道是提供 AppleComputer 的工廠類,對(duì)應(yīng)著我們 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");
}
```
參數(shù) MainModule 就是剛才在 builder 方法中初始化的那個(gè) MainModule。最終返回的是 MainModule 的 providesAppleComputer 方法所提供的實(shí)例對(duì)象。至此,Component 就完成了從 Module 中獲取依賴實(shí)例對(duì)象,并注入到依賴需求方的過程。
## 總結(jié)
1、Component的實(shí)現(xiàn)類采用Builder模式,完成Component成員變量Module的設(shè)置。
2、Component實(shí)現(xiàn)類的inject方法,參數(shù)為等待注入的對(duì)象(如Activity等),將Module提供的對(duì)象設(shè)置給等待注入的對(duì)象。
- 導(dǎo)讀
- Java知識(shí)
- Java基本程序設(shè)計(jì)結(jié)構(gòu)
- 【基礎(chǔ)知識(shí)】Java基礎(chǔ)
- 【源碼分析】Okio
- 【源碼分析】深入理解i++和++i
- 【專題分析】JVM與GC
- 【面試清單】Java基本程序設(shè)計(jì)結(jié)構(gòu)
- 對(duì)象與類
- 【基礎(chǔ)知識(shí)】對(duì)象與類
- 【專題分析】Java類加載過程
- 【面試清單】對(duì)象與類
- 泛型
- 【基礎(chǔ)知識(shí)】泛型
- 【面試清單】泛型
- 集合
- 【基礎(chǔ)知識(shí)】集合
- 【源碼分析】SparseArray
- 【面試清單】集合
- 多線程
- 【基礎(chǔ)知識(shí)】多線程
- 【源碼分析】ThreadPoolExecutor源碼分析
- 【專題分析】volatile關(guān)鍵字
- 【面試清單】多線程
- Java新特性
- 【專題分析】Lambda表達(dá)式
- 【專題分析】注解
- 【面試清單】Java新特性
- Effective Java筆記
- Android知識(shí)
- Activity
- 【基礎(chǔ)知識(shí)】Activity
- 【專題分析】運(yùn)行時(shí)權(quán)限
- 【專題分析】使用Intent打開三方應(yīng)用
- 【源碼分析】Activity的工作過程
- 【面試清單】Activity
- 架構(gòu)組件
- 【專題分析】MVC、MVP與MVVM
- 【專題分析】數(shù)據(jù)綁定
- 【面試清單】架構(gòu)組件
- 界面
- 【專題分析】自定義View
- 【專題分析】ImageView的ScaleType屬性
- 【專題分析】ConstraintLayout 使用
- 【專題分析】搞懂點(diǎn)九圖
- 【專題分析】Adapter
- 【源碼分析】LayoutInflater
- 【源碼分析】ViewStub
- 【源碼分析】View三大流程
- 【源碼分析】觸摸事件分發(fā)機(jī)制
- 【源碼分析】按鍵事件分發(fā)機(jī)制
- 【源碼分析】Android窗口機(jī)制
- 【面試清單】界面
- 動(dòng)畫和過渡
- 【基礎(chǔ)知識(shí)】動(dòng)畫和過渡
- 【面試清單】動(dòng)畫和過渡
- 圖片和圖形
- 【專題分析】圖片加載
- 【面試清單】圖片和圖形
- 后臺(tái)任務(wù)
- 應(yīng)用數(shù)據(jù)和文件
- 基于網(wǎng)絡(luò)的內(nèi)容
- 多線程與多進(jìn)程
- 【基礎(chǔ)知識(shí)】多線程與多進(jìn)程
- 【源碼分析】Handler
- 【源碼分析】AsyncTask
- 【專題分析】Service
- 【源碼分析】Parcelable
- 【專題分析】Binder
- 【源碼分析】Messenger
- 【面試清單】多線程與多進(jìn)程
- 應(yīng)用優(yōu)化
- 【專題分析】布局優(yōu)化
- 【專題分析】繪制優(yōu)化
- 【專題分析】?jī)?nèi)存優(yōu)化
- 【專題分析】啟動(dòng)優(yōu)化
- 【專題分析】電池優(yōu)化
- 【專題分析】包大小優(yōu)化
- 【面試清單】應(yīng)用優(yōu)化
- Android新特性
- 【專題分析】狀態(tài)欄、ActionBar和導(dǎo)航欄
- 【專題分析】應(yīng)用圖標(biāo)、通知欄適配
- 【專題分析】Android新版本重要變更
- 【專題分析】唯一標(biāo)識(shí)符的最佳做法
- 開源庫源碼分析
- 【源碼分析】BaseRecyclerViewAdapterHelper
- 【源碼分析】ButterKnife
- 【源碼分析】Dagger2
- 【源碼分析】EventBus3(一)
- 【源碼分析】EventBus3(二)
- 【源碼分析】Glide
- 【源碼分析】OkHttp
- 【源碼分析】Retrofit
- 其他知識(shí)
- Flutter
- 原生開發(fā)與跨平臺(tái)開發(fā)
- 整體歸納
- 狀態(tài)及狀態(tài)管理
- 零碎知識(shí)點(diǎn)
- 添加Flutter到現(xiàn)有應(yīng)用
- Git知識(shí)
- Git命令
- .gitignore文件
- 設(shè)計(jì)模式
- 創(chuàng)建型模式
- 結(jié)構(gòu)型模式
- 行為型模式
- RxJava
- 基礎(chǔ)
- Linux知識(shí)
- 環(huán)境變量
- Linux命令
- ADB命令
- 算法
- 常見數(shù)據(jù)結(jié)構(gòu)及實(shí)現(xiàn)
- 數(shù)組
- 排序算法
- 鏈表
- 二叉樹
- 棧和隊(duì)列
- 算法時(shí)間復(fù)雜度
- 常見算法思想
- 其他技術(shù)
- 正則表達(dá)式
- 編碼格式
- HTTP與HTTPS
- 【面試清單】其他知識(shí)
- 開發(fā)歸納
- Android零碎問題
- 其他零碎問題
- 開發(fā)思路
