[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零碎問題
- 其他零碎問題
- 開發思路