Dagger 源碼解析

http://a.codekk.com/detail/Android/%E6%89%94%E7%89%A9%E7%BA%BF/Dagger%20%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90

本文爲 Android 開源項目源碼解析 中 Dagger 部分
項目地址:Dagger,分析的版本:2f9579c,Demo 地址:Dagger Demo
分析者:扔物線,校對者:Trinea,校對狀態:完成

1. 功能介紹

1.1 Dagger

Dagger 是一款 Java 平臺的依賴注入庫,關於依賴注入,詳細見 依賴注入簡介

Java 的依賴注入庫中,最有名的應該屬 Google 的 Guice,Spring 也很有名,不過是專注於 J2EE 開發。Guice 的功能非常強大,但它是通過在運行時讀取註解來實現依賴注入的,依賴的生成和注入需要依靠 Java 的反射機制,這對於對性能非常敏感的 Android 來說是一個硬傷。基於此,Dagger 應運而生。

Dagger 同樣使用註解來實現依賴注入,但它利用 APT(Annotation Process Tool) 在編譯時生成輔助類,這些類繼承特定父類或實現特定接口,程序在運行時 Dagger 加載這些輔助類,調用相應接口完成依賴生成和注入。Dagger 對於程序的性能影響非常小,因此更加適用於 Android 應用的開發。

1.2 依賴注入相關概念

依賴(Dependency):如果在 Class A 中,有個屬性是 Class B 的實例,則稱 Class B 是 Class A 的依賴,本文中我們將 Class A 稱爲宿主(Host),並且全文用 Host 表示;Class B 稱爲依賴(Dependency),並且全文用 Dependency 表示。一個 Host 可能是另外一個類的 Dependency。

宿主(Host):如果 Class B 是 Class A 的 Dependency,則稱 Class A 是 Class B 的宿主(Host)。

依賴注入:如果 Class B 是 Class A 的 Dependency,B 的賦值不是寫死在了類或構造函數中,而是通過構造函數或其他函數的參數傳入,這種賦值方式我們稱之爲依賴注入。

更詳細介紹可見 依賴注入簡介

1.3 Dagger 基本使用

本文將以一個簡單的“老闆和程序員” App 爲例。

Activity 中有一個 Boss 類屬性,現在你想把一個 Boss 對象注入到這個 Activity 中,那麼有兩個問題需要解決:Boss 對象應該怎樣被生成 以及 Boss 對象怎樣被設置到 Activity 中。

(1). Boss 對象怎樣生成

在 Boss 類的構造函數前添加一個 @Inject 註解,Dagger 就會在需要獲取 Boss 對象時,調用這個被標記的構造函數,從而生成一個 Boss 對象。

public class Boss {
    ...

    @Inject
    public Boss() {
        ...
    }

    ...
}

需要注意的是,如果構造函數含有參數,Dagger 會在調用構造對象的時候先去獲取這些參數(不然誰來傳參?),所以你要保證它的參數也提供可被 Dagger 調用到的生成函數。Dagger 可調用的對象生成方式有兩種:一種是用 @Inject 修飾的構造函數,上面就是這種方式。另外一種是用 @Provides 修飾的函數,下面會講到。

(2). Boss 對象怎樣被設置到 Activity 中

通過 @Inject 註解了構造函數之後,在 Activity 中的 Boss 屬性聲明之前也添加 @Inject 註解。像這種在屬性前添加的 @Inject 註解的目的是告訴 Dagger 哪些屬性需要被注入。

public class MainActivity extends Activity {
    @Inject Boss boss;
    ...
}

最後,我們在合適的位置(例如 onCreate() 函數中)調用 ObjectGraph.inject() 函數,Dagger 就會自動調用上面 (1) 中的生成方法生成依賴的實例,並注入到當前對象(MainActivity)。

public class MainActivity extends Activity {
    @Inject Boss boss;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        ObjectGraph.create(AppModule.class).inject(this);
    }
    ...
}

具體怎麼注入即設置的過程後面會詳細介紹,這裏簡單透露下,APT 會在 MainActivity 所在 package 下生成一個輔助類 MainActivity$$InjectAdapter,這個類有個 injectMembers() 函數,代碼類似:

public void injectMembers(MainActivity paramMainActivity) {
    paramMainActivity.boss = ((Boss)boss.get());
    ……
}

上面我們已經通過 ObjectGraph.inject() 函數傳入了 paramMainActivity,並且 boss 屬性是 package 權限,所以 Dagger 只需要調用這個輔助類的 injectMembers() 函數即可完成依賴注入,這裏的 boss.get() 會調用 Boss 的生成函數。
到此爲止,使用 Dagger 的 @Inject 方式將一個 Boss 對象注入到 MainActivity 的流程就完成了。

(3). ObjectGraph.create(AppModule.class) 函數簡介

上面 onCreate() 函數中出現了兩個類:ObjectGraph 和 AppModule。其中 ObjectGraph 是由 Dagger 提供的類,可以簡單理解爲一個依賴管理類,它的 create() 函數的參數是一個數組,爲所有需要用到的 Module(例如本例中的 AppModule)。AppModule 是一個自定義類,在 Dagger 中稱爲Module,通過 @Module 註解進行標記,代碼如下:

@Module(injects = MainActivity.class)
public class AppModule {
}

可以看到,AppModule 是一個空類,除了一行註解外沒有任何代碼。
@Module 註解表示這個類是一個Module,Module 的作用是提供信息,讓 ObjectGraph 知道哪些類對象需要被依賴注入,以及該怎麼生成某些依賴(這在下面會具體介紹)。例如,上面這段代碼中聲明瞭需要依賴注入的類爲 MainActivity。
需要在 Module 類中顯式聲明這些信息看起來很麻煩,多此一舉的方式和 Dagger 的原理有關,下面會講到。

1.4 自定義依賴生成方式

(1). @Provides 修飾的生成函數

對構造函數進行註解是很好用的依賴對象生成方式,然而它並不適用於所有情況。例如:

  • 接口(Interface)是沒有構造函數的,當然就不能對構造函數進行註解
  • 第三方庫提供的類,我們無法修改源碼,因此也不能註解它們的構造函數
  • 有些類需要提供統一的生成函數(一般會同時私有化構造函數)或需要動態選擇初始化的配置,而不是使用一個單一的構造函數

對於以上三種情況,可以使用 @Provides 註解來標記自定義的生成函數,從而被 Dagger 調用。形式如下:

@Provides
Coder provideCoder(Boss boss) {
    return new Coder(boss);
}

和構造函數一樣,@Provides 註解修飾的函數如果含有參數,它的所有參數也需要提供可被 Dagger 調用到的生成函數。
需要注意的是,所有 @Provides 註解的生成函數都需要在Module中定義實現,這就是上面提到的 Module 的作用之一——讓 ObjectGraph 知道怎麼生成某些依賴。

@Module
public class AppModule {
    @Provides
    Coder provideCoder(Boss boss) {
        return new Coder(boss);
    }
}
(2). @Inject 和 @Provide 兩種依賴生成方式區別

a. @Inject 用於注入可實例化的類,@Provides 可用於注入所有類
b. @Inject 可用於修飾屬性和構造函數,可用於任何非 Module 類,@Provides 只可用於用於修飾非構造函數,並且該函數必須在某個Module內部
c. @Inject 修飾的函數只能是構造函數,@Provides 修飾的函數必須以 provide 開頭

1.5 單例

Dagger 支持單例(事實上單例也是依賴注入最常用的場景),使用方式也很簡單:

// @Inject 註解構造函數的單例模式
@Singleton
public class Boss {
    ...

    @Inject
    public Boss() {
        ...
    }

    ...
}
// @Provides 註解函數的單例模式
@Provides
@Singleton
Coder provideCoder(Boss boss) {
    return new Coder(boss);
}

在相應函數添加 @Singleton 註解,依賴的對象就只會被初始化一次,之後的每次都會被直接注入相同的對象。

1.6 Qualifier(限定符)

如果有兩類程序員,他們的能力值 power 分別是 5 和 1000,應該怎樣讓 Dagger 對他們做出區分呢?使用 @Qualifier 註解即可。

(1). 創建一個 @Qualifier 註解,用於區分兩類程序員:

@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Level {
  String value() default "";
}

(2). 爲這兩類程序員分別設置 @Provides 函數,並使用 @Qualifier 註解對他們做出不同的標記:

@Provides @Level("low") Coder provideLowLevelCoder() {
    Coder coder = new Coder();
    coder.setName("戰五渣");
    coder.setPower(5);
    return coder;
}

@Provides @Level("high") Coder provideHighLevelCoder() {
    Coder coder = new Coder();
    coder.setName("大神");
    coder.setPower(1000);
    return coder;
}

(3). 在聲明 @Inject 對象的時候,加上對應的 @Qualifier 註解。

@Inject @Level("low") Coder lowLevelCoder;
@Inject @Level("high") Coder highLevelCoder;

1.7 編譯時檢查

實質上,Dagger 會在編譯時對代碼進行檢查,並在檢查不通過的時候報編譯錯誤,具體原因請看下面的詳細原理介紹。檢查內容主要有三點:
(1). 所有需要依賴注入的類,需要被顯式聲明在相應的Module中。
(2). 一個Module中所有 @Provides 函數的參數都必須在這個 Module 中提供相應的被 @Provides 修飾的函數,或者在 @Module 註解後添加 "complete = false" 註明這是一個不完整 Module,表示它依賴不屬於這個 Module 的其他 Denpendency。
(3). 一個Module中所有的 @Provides 函數都要被它聲明的注入對象所使用,或者在 @Module 註解後添加 "library = ture" 註明它含有對外的 Denpendency,可能被其他Module依賴。

1.8 Dagger 相關概念

Module:也叫 ModuleClass,指被 @Module 註解修飾的類,爲 Dagger 提供需要依賴注入的 Host 信息及一些 Dependency 的生成方式。

ModuleAdapter:指由 APT 根據 @Module 註解自動生成的類,父類是 Dagger 的 ModuleAdapter.java,與 ModuleClass 對應,以 ModuleClass 的 ClassName 加上 $$ModuleAdapter 命名,在 ModuleClass 的同一個 package 下。

Binding:指由 APT 根據 @Inject 註解和 @Provides 註解自動生成,最終繼承自 Binding.java 的類。爲下面介紹的 DAG 圖中的一個節點,每個 Host 及依賴都是一個 Binding。

InjectAdapter:每個屬性或構造函數被 @Inject 修飾的類都會生成一個 繼承自 Binding.java 的子類,生成類以修飾類的 ClassName 加上 $$InjectAdapter 命名,在該類的同一個 package 下。

ProvidesAdapter:每個被 @Provides 修飾的生成函數都會生成一個繼承自 ProvidesBinding.java 的子類,ProvidesBinding.java 繼承自 Binding.java,生成類以 Provide 函數名首字母大寫加上 ProvidesAdapter 命名,是 Provide 函數所在 Module 對應生成的ModuleAdapter中的靜態內部類。
Binding 更具體信息在下面會介紹。

Binding 安裝:指將 Binding 添加到 Binding 庫中。對 Dagger Linker.java 代碼來說是將 Binding 添加到 Linker.bindings 屬性中,Linker.bindings 屬性表示某個 ObjectGraph 已安裝的所有 Binding。對於下面的 DAG 圖來說是將節點放到圖中,但尚未跟其他任何節點連接起來。

Binding 連接:把當前 Binding 和它內部依賴的 Binding 進行連接,即初始化這個 Binding 內部的所有 Binding,使它們可用。對 DAG 的角度說,就是把某個節點與其所依賴的各個節點連接起來。

2. 總體設計

2.1 概述

事實上,Dagger 這個庫的取名不僅僅來自它的本意“匕首”,同時也暗示了它的原理。Jake Wharton 在對 Dagger 的介紹中指出,Dagger 即 DAG-er,這裏的 DAG 即數據結構中的 DAG——有向無環圖(Directed Acyclic Graph)。也就是說,Dagger 是一個基於有向無環圖結構的依賴注入庫。

2.2 DAG(有向無環圖)

已經瞭解 DAG 的可以跳過這節。
DAG 是數據結構的一種。在一組節點中,每一個節點指向一個或多個節點,但不存在一條正向的鏈最終重新指向自己(即不存在環),這樣的結構稱爲有向無環圖,即 DAG。

DAG

上圖中的數據結構就是一個有向無環圖。圖中一共存在 6 個節點和 7 個箭頭,但任何一個節點都無法從自己發射出的箭頭通過某條迴路重新指向自己。

2.3 Dagger 中依賴注入與 DAG 的關係

Dagger 的運作機制,是運用 APT(Annotation Process Tool) 在編譯時生成一些用於設定規則的代碼,然後在運行時將這些規則進行動態組合 // TODO 不太理解意思,生成一個(或多個)DAG,然後由 DAG 來完成所有依賴的獲取,實現依賴注入。關於 DAG 究竟是怎樣一步步生成的,後面再講,這裏先說一下在 Dagger 中依賴注入與 DAG 的關係。

DAG-DI

我把前面那張圖的每個節點重新命名,得到了上圖。上圖代表了某個應用程序內的一整套依賴關係,其中每個箭頭都表示兩個類之間依賴關係,Host 和 Dependency 都是其中的一個節點。

可以看出,一個程序中的整套依賴關係其實就是一個 DAG。而實際上,Dagger 也是這麼做的:預先建立一個 DAG,然後在需要獲取對象的時候通過這個依賴關係圖來獲取到對象並返回,若獲取失敗則進行查找,查找到後再補充到 DAG 中。

Dagger 是支持傳遞依賴的。例如在上圖中,當需要獲取一個 CustomView,會首先獲取一個 DataHelper 作爲獲取 CustomView 的必要參數;此時如果 DataHelper 還未初始化,則還要分別拿到 HttpHelper 和 Database 用來初始化 DataHelper;以此類推。

Dagger 不支持循環依賴,即依賴關係圖中不能出現環。原因很簡單,如果雞依賴蛋,蛋依賴雞,誰來創造世界?總有一個要先產生的。

2.4 工作流程

(1). 編譯時,通過 APT 查看所有 java 文件,並根據註解生成一些新的 java 文件,即InjectAdapterProvidesAdapterModuleAdapter,這些文件用於運行時輔助 DAG 的創建和完善。然後,將這些新生成的 java 文件和項目原有的 java 文件一併編譯成 class 文件。
(2). 運行時,在 Application 或某個具體模塊的初始化處,使用ObjectGraph類來加載部分依賴(實質上是利用編譯時生成的ModuleAdapters加載了所有的ProvidesBinding,後面會講到),形成一個不完整的依賴關係圖。
(3). 這個不完整的依賴關係圖生成之後,就可以調用ObjectGraph的相應函數來獲取實例和注入依賴了。實現依賴注入的函數有兩個:ObjectGraph.get(Class<T> type)函數,用於直接獲取對象;ObjectGraph.inject(T instance)函數,用於對指定對象進行屬性的注入。在這些獲取實例和注入依賴的過程中,如果用到了還未加載的依賴,程序會自動對它們進行加載(實質上是加載的編譯時生成的InjectAdapter)。在此過程中,內存中的 DAG 也被補充地越來越完整。

3. 流程圖

3.1 編譯時:

dagger_flow_chart_compile

3.2 運行時(初始化後):

dagger_flow_chart_runtime

4. 詳細設計

4.1 類關係圖

uml_types

上圖是 Dagger 整體框架最簡類關係圖。大致原理可以描述爲:Linker通過Loader加載需要的Binding並把它們拼裝成合理的依賴關係圖 ObjectGraph,由ObjectGraph(其子類DaggerObjectGraph)最終實現依賴注入的管理。
ObjectGraph 是個抽象類,DaggerObjectGraph 是它目前唯一的子類,對 Dagger 的調用實際都是對 DaggerObjectGraph 的調用。

4.2 類功能詳細介紹

4.2.1 Binding.java —— 節點

Binding 是一個泛型抽象類,相當於依賴關係 DAG 圖中的節點,依賴關係 DAG 圖中得每一個節點都有一個由 APT 生成的繼承自 Binding 的類與之對應,而依賴關係 DAG 圖中的每一個節點與HostDependency一一對應,所以每個HostDependency必然有一個由 APT 生成的繼承自 Binding 的子類與之對應,我們先簡單的將這些生成類分爲HostBindingDependencyBinding

(1). Binding.java 實現的接口

Binding.java 實現了兩個接口,第一個是 javax 的Provider接口,此接口提供了 get() 函數用於返回一個Dependency實例,當然也可以是Host實例。
第二個接口是 Dagger 中的MembersInjector接口,此接口提供了 injectMembers() 用來向Host對象中注入(即設置)Dependency
單純的DependencyBinding只要實現Provider接口,在 get() 函數中返回自己的實例即可。單純的HostBinding只要實現MembersInjector,在 injectMembers() 函數中調用DependencyBinding的 get() 函數得到依賴,然後對自己的依賴進行注入即可。如果一個類既是Host又是Dependency,則與它對應的Binding這兩個接口都需要實現。

(2). 生成的 Binding 代碼示例

如下的 Host 和 Dependency 類

public class Host {
    @Inject Dependency dependency;
}

public class Dependency {
    @Inject
    public Dependency() {
        ……
    }
}

由 APT 生成的 Binding 應該類似

public final class Host$$InjectAdapter extends Binding<Host> implements MembersInjector<Host> {
    private Binding<Dependency> dependencyBinding;

    ……

    public void attach(Linker linker) {
        dependencyBinding = (Dependency$$InjectAdapter)linker.requestBinding(……);
    }

    public void injectMembers(Host host) {
        host.dependency = (Dependency)dependencyBinding.get();
    }
}
public final class Dependency$$InjectAdapter extends Binding<Dependency> implements Provider<Dependency> {

    ……

    public Dependency get() {
        return new Dependency();
    }
}

HostBinding指的是生成類 Host$$InjectAdapter,DependencyBinding指的是生成類 Dependency$$InjectAdapter,我們可以看到HostBinding的 attach 方法用於得到DependencyBinding的實例,然後在 injectMembers() 函數中通過調用這個實例的 get() 函數注入 Dependency,DependencyBinding 的 get() 函數就是調用Dependency的生成方法。

(3). Binding 分類

上面我們將生成的 Binding 子類簡單分爲了HostBindingDependencyBinding,實際根據前面的注入方式我們知道依賴的生成方式有 @Inject 和 @Provides 兩種,對這兩種方式,Dagger 生成 Binding 子類的規則不同。

對於 @Inject 方式的注入,APT 會在Dependency同一個 package 下以Dependency的 ClassName 加上 $$InjectAdapter 爲類名生成一個 Binding 子類。 對於 @Provides 方式的注入,@Provides 的生成函數必須寫在某個Module內部,與此 Module 對應的ModuleAdapter(Module$$ModuleAdapter)內部會有一個此 @Provides 方式對應的 Binding 子類,繼承自 Binding 的子類 ProvidesBinding,以 @Provides 函數名首字母大寫加上 ProvidesAdapter 命名。

所以實際自動生成的 Binding 子類我們可以分爲三種:
第一種是Host對應的 Binding,本文中我們統一稱爲HostBinding。這些HostBinding和被 @Module 修飾的Module injects 值中每個元素一一對應,他們提供 get()、injectMembers()、attach() 函數。

第二種是 Inject Dependecy 對應的 Binding 子類,本文中我們統一稱爲InjectBinding。這些InjectBinding和所有含有 @Inject 修飾的構造函數的類一一對應,他們提供 get() 函數,不提供 injectMembers() 函數。如果它同時是個Host,也會提供 injectMembers() 函數。

第三種是 Provide Dependecy 對應的 Binding 子類,本文中我們統一稱爲ProvidesBindingProvidesBinding 和 @Module 類中的被 @Provides 修飾的函數一一對應,他們只提供 get() 函數,不提供 injectMembers() 函數。
上面三種 Binding 中,第一、二種會在 ObjectGraph.create 時加載進來,第三種在用的時候纔會被動態加載。InjectBindingProvidesBinding統稱爲DependencyBinding

Binding.java 的主要函數:

(1). get()

表示得到此 Binding 對應的DependencyInjectBinding會在 get() 中調用被 @Inject 修飾的構造函數,ProvidesBinding會在 get() 函數中調用被 @Provides 修飾的生成函數。

(2). injectMembers(T t)

表示向此 Binding 對應Host對象中注入依賴,這個函數的實現一般就是對被 @Inject 修飾的屬性進行賦值,值爲DependencyBinding的 get() 函數返回值。

(3). attach(Linker linker)

表示HostBinding獲取依賴的 Binding 即DependencyBinding對象,對於 DAG 圖來說相當於把圖中兩個節點連接起來。對於DependencyBinding此函數爲空。

(4). getDependencies(…)

表示HostBinding得到依賴的DependencyBinding),這個函數在對 DAG 圖進行問題檢測,比如循環依賴檢測時用到。

Binding.java 的主要屬性:

(1). provideKey

表示 Binding 所屬 Host 或 Dependency 的類名,是 Binding 唯一的 key,在 Linker 管理 Binding 時會用到,作爲存儲所有 Binding 的 Map 的 key。對HostBinding值爲 HostClassName.toString(),DependencyBinding值爲 DependencyClassName.toString()。

(2). membersKey

// TODO。對HostBinding值爲 members/ 加上 HostClassName.toString(),InjectBinding值爲 members/ 加上 DependencyClassName.toString(),ProvidesBinding值爲 null。ProvidesBinding值爲 null,因爲它默認就連接好了。

(3). requiredBy

表示這個 Binding 屬於誰,對HostBinding值爲 HostClass.class,InjectBinding值爲 DependencyClass.class,ProvidesBinding值爲 ProvideMethodName.toString()。

(4). bits

表示 Binding 特性的標誌位,如是是否是單例(SINGLETON)、是否已連接(LINKED),是否被訪問(VISITING)、是否是可被其他 Module 依賴的 Library(LIBRARY)、是否依賴其他 Module 的 Binding(DEPENDED_ON)、是否不存在循環依賴(CYCLE_FREE)。

4.2.2 Linker.java —— 拼裝者

Linker 是 Dagger 最核心的大腦部分,它負責調用 Loader 加載 Binding,存儲並管理所有 Binding、調用 attach 方法初始化依賴的 DependencyBinding。對於 DAG 圖來說,Linker 就相當於一個管家,負責調用加載器加載節點到圖中、存儲並管理圖中所有的節點,連接圖中有依賴關係的節點,也就是 DAG 圖的拼裝。
Dagger 在運行時維護一個或多個Linker,Linker 與 ObjectGraph 一一對應。

Linker.java 的主要屬性:

(1). bindings

本文稱爲 ObjectGraph 的 Binding 庫,表示 ObjectGraph 已安裝的所有 Binding,包括尚未連接的 Binding,對於 DAG 圖來說就是所有在圖中的節點,包括尚未跟其他任何節點連接起來的節點。
bindings 數據結構爲 HashMap,value 就是具體的 Binding,key 是用來唯一確定 Binding 的字符串,爲 Binding.java 中的 provideKey 和 membersKey,具體形式是類名加上一個用於區分同類型的前綴。這些 Binding 不僅包含已連接的,也包含未連接的。

表示待連接的 Binding 隊列,包含了所有待連接的 Binding。對於 DAG 圖來說就是所有在圖中但未和任何節點連接的節點。

連接(Link):從 DAG 的角度說,就是把某個節點與其所依賴的各個節點連接起來。而對於 Binding 來說,就是把當前 Binding 和它依賴的 Binding (ProvidesBinding)進行連接,即初始化這個 Binding 依賴的所有 Binding,使它們可用。

(3). attachSuccess

一個標誌,對於某個 Binding,在獲取它依賴的DependencyBinding時,如果他所有的DependencyBinding都已經添加到Binding庫中,attachSuccess 則爲 true,否則爲 false。如果爲 false,表示該 Binding 尚未連接,添加到待連接隊列中,否則標記爲已連接。

(4). linkedBindings

默認爲 null,只有在 linkAll() 函數被調用後纔有效,用於存儲所有已經連接的 Binding,同時也是一個標記,表示這個 ObjectGraph 已經不能被改變。

(5). Loader plugin

Loader 負責加載類,主要是加載 APT 生成的輔助類(InjectAdapter、ModuleAdapter)。

(6). errors

Linker.linkRequested() 運行過程中積累的 errors。

Linker.java 的主要函數:

(1). requestBinding(String key ……)

根據傳入的 key 返回一個 Binding。首先,會嘗試從 Bindings 變量(Binding 庫)中查找這個 key,如果找到了,就將找到的 Binding 返回(如果找到後發現這個 Binding 還未連接,還需要它放進 toLink 中);如果找不到,說明需要的 Binding 是一個InjectBinding(因爲另一種 Binding——ProvidesBinding 在初始化時就已經加載完畢了),就生成一個包含了這個 key 的DeferredBinding,並把它添加到 toLink(等待稍後載入)後返回 null。

(2). linkRequested()

循環取出 toLink 中的 Binding:
如果是個DeferredBinding載入相應的InjectAdapter後添加到toLinkbindings中,等待下次循環。
否則調用 attach 函數進行連接,對於DependencyBinding連接完成。對於HostBinding利用 attach() 函數獲取依賴的 Binding 即DependencyBinding對象,在獲取DependencyBinding的過程中調用 requestBinding() 函數查找 Binding,不存在或未連接會繼續添加到 toLink 隊列中,如此循環。
直到所有依賴DependencyBinding被初始化結束。
對 DAG 圖來說就是一次廣度優先遍歷。

(3). installBindings(BindingsGroup toInstall)

安裝 Bindings,表示將 Binding 添加到 ObjectGraph 中,但尚未連接。對 DAG 圖來說就是就是將節點放到圖中,但尚未和任何其他節點連接。

(4). linkAll()

將 Binding 庫中所有未連接的 Binding 添加到 toLink 中,調用 linkRequested() 進行連接。

(5). fullyLinkedBindings()

返回已經全部連接的 Binding,如果沒有調用過 linkAll() 則返回 null

4.2.3 Loader.java —— 類加載器及對象生成器

Loader 是一個純工具類,它通過 ClassLoader 加載 APT 生成的ModuleAdapter類和InjectAdapter類,並初始化一個該類對象返回。另外,Loader 是一個抽象類,在運行時,Dagger 使用的是 Loader 的子類FailoverLoader

Loader.java 的主要函數:

(1). loadClass(ClassLoader classLoader, String name)

用指定的 ClassLoader 根據類名得到類,並緩存起來。

(2). instantiate(String name, ClassLoader classLoader)

用指定的 ClassLoader 根據類名獲取類的實例。

(3). getModuleAdapter(Class moduleClass)

獲取指定的 Module 類所對應的 ModuleAdapter 實例。

(4). getAtInjectBinding(String key……)

根據 key 獲取 Inject Dependecy 對應的 InjectAdapter 實例。

(5). getStaticInjection(Class<?> injectedClass)

根據被注入的 Class 獲取對應的 StaticInjection 實例。

Loader.java 的主要變量:

(1). Memoizer<classloader, memoizer<string,="" class<?="" style="box-sizing: border-box;">>> caches

用來緩存被初始化過的對象,是一個嵌套的 Memoizer 結構,Memoizer具體可看後面介紹,簡單理解就是嵌套的 HashMap,第一層 Key 是 ClassLoader,第二層 Key 是 ClassName,Value 是 Class 對象。

4.2.4 FailoverLoader.java

FailoverLoader 是 Loader 的一個子類,它加載類的策略是首先查找 APT 生成的類,如果查找失敗,則直接使用反射查找和初始化。
FailoverLoader.java 的主要函數:

(1). getModuleAdapter(Class moduleClass)

獲取指定的 Module 類所對應的 ModuleAdapter 實例,如果在生成類中查找失敗,則會調用 ReflectiveAtInjectBinding.create(type, mustHaveInjections) 通過反射直接初始化對象。

(2). getAtInjectBinding(String key……)

根據 key 獲取 Inject Dependecy 對應的 InjectAdapter 實例。如果在生成類中查找失敗,則會調用 ReflectiveStaticInjection.create(injectedClass) 通過反射直接初始化對象。

(3). getStaticInjection(Class<?> injectedClass)

根據被注入的 Class 獲取對應的 StaticInjection 實例。

FailoverLoader.java 的主要變量:

(1). Memoizer<class<? style="box-sizing: border-box;">, ModuleAdapter<?>> loadedAdapters

用來緩存初始化過的 ModuleAdapter 對象,是一個嵌套的 Memoizer 結構,具體可看下面介紹,簡單理解就是嵌套的 HashMap,第一層 Key 是 ClassLoader,第二層 Key 是 ClassName,Value 是 Class 對象。

4.2.5 ObjectGraph —— 管理者

ObjectGraph 是個抽象類,負責 Dagger 所有的業務邏輯,Dagger 最關鍵流程都是從這個類發起的,包括依賴關係圖創建、實例(依賴或宿主)獲取、依賴注入。
ObjectGraph 主要函數有:

(1). create(Object... modules)

這是個靜態的構造函數,用於返回一個 ObjectGraph 的實例,是使用 Dagger 調用的第一個函數。參數爲 ModuleClass 對象,函數作用是根據 ModuleClass 構建一個依賴關係圖。此函數實現會直接調用

DaggerObjectGraph.makeGraph(null, new FailoverLoader(), modules)

返回一個DaggerObjectGraph對象,我們會在下面DaggerObjectGraph介紹中具體介紹實現過程。

(2). inject(T instance)

抽象函數,表示向某個 Host 對象中注入依賴。

(3). injectStatics()

抽象函數,表示向 ObjectGraph 中相關的 Host 注入靜態屬性。

(4). get(Class type)

抽象函數,表示得到某個對象的實例,多用於得到依賴的實例。

(5). plus(Object... modules)

抽象函數,表示返回一個新的包含當前 ObjectGraph 中所有 Binding 的 ObjectGraph。

(6). validate()

抽象函數,表示對當前 ObjectGraph 做檢查。

4.2.6 DaggerObjectGraph

DaggerObjectGraph 是 ObjectGraph 的靜態內部類,也是 ObjectGraph 目前唯一的子類。因爲 ObjectGraph 的 create() 函數直接返回了 DaggerObjectGraph 對象,所以對 Dagger 的調用實際都是對 DaggerObjectGraph 的調用。
DaggerObjectGraph 主要屬性有:

(1). Map injectableTypes

記錄了所有需要被依賴注入的 Host 類型,以 Host 的 ClassName 加上一定規則前綴(// TODO)做爲 key,以其所對應的 Module 爲 value。

(2). Map staticInjections

記錄了所有需要被靜態依賴注入的 Host 類型,以 Host 的 ClassName 加上一定規則前綴(// TODO)做爲 key,以其所對應的 Module 爲 value。

(3). Linker linker

Linker 是 負責調用 Loader 加載 Binding,存儲並管理所有 Binding、調用 attach 方法初始化依賴的 DependencyBinding。具體見上面Linker.java介紹。

(4). Loader plugin

Loader 負責通過 ClassLoader 加載 APT 生成的ModuleAdapter類和InjectAdapter類。
PS:這個變量名叫 plugin,實際也說明了 Dagger 的一大優勢,就是它是支持 ClassLoader,這樣通過 Dagger 實現依賴注入的 Android 應用,插件化時 Dagger 不會對其產生影響,而截止這個分析文檔完成時,輕量級的 ButterKnife 都不支持多個 ClassLoader。
DaggerObjectGraph 主要函數有:

(1). makeGraph 函數

makeGraph 函數首先會通過 Modules.loadModules 函數得到所有的 ModuleAdapter;
然後遍歷所有 ModuleAdapter,將其中需要依賴注入的 Host 類型(injectableTypes)、需要靜態靜態注入的 Host 類型(staticInjections)、所有的 Binding(這裏是ProvidesBinding)都保存下來,做爲新的 DaggerObjectGraph 對象構造入參。另一種 Binding —— InjectBinding 會在需要用到的時候進行動態載入;
第三步新建 Linker 保存上面的 Binding;
最後用這些變量一起構建新的 DaggerObjectGraph 對象。

(2). inject(T instance)

表示向某個 Host 對象中注入依賴。首先根據下面的 getInjectableTypeBinding() 函數查找到 Host 對應的 InjectBinding,然後調用 injectMembers() 函數注入依賴,將依賴注入結束的 Host 返回。

(3). injectStatics()

表示向 ObjectGraph 中相關的 Host 注入靜態屬性。

(4). get(Class type)

表示得到某個對象的實例,多用於得到 Denpendency 的實例。首先根據下面的 getInjectableTypeBinding() 函數查找到 Denpendency 對應的 Binding,然後調用 get() 返回該 Denpendency 實例。

(5). plus(Object... modules)

抽象函數,表示返回一個新的包含當前 ObjectGraph 中所有對象的 ObjectGraph。

(6). validate()

表示對當前 ObjectGraph 做檢查,首先會利用 Linker 查找到所有節點並連接起來,然後調用 ProblemDetector 進行檢查。ProblemDetector 會在後面解釋作用。

(7). getInjectableTypeBinding(ClassLoader classLoader, String injectableKey, String key)

表示根據 key 得到某個 Binding。首先會從 ObjectGraph.injectableTypes 中得到其對應的 Module,然後通過 linker.requestBinding 查找其對應的 Binding,若未查找到的 Binding 或是尚未連接,則調用 linker.linkRequested() 得到 InjectBindng 並將其添加到 ObjectGraph 中,此時再次通過 linker.requestBinding 即可查找到其對應的 Binding,返回即可。

(8). linkInjectableTypes()

查找 injectableTypes 記錄的所有需要被依賴注入的 Host 類型對應的HostBinding

(9). linkStaticInjections()

查找 staticInjections 記錄的所有需要被靜態依賴注入的 Host 類型對應的HostBinding

(10) linkEverything()

首先檢查是否連接過,沒有的話,則先調用 linkInjectableTypes() 和 linkStaticInjections() 將所有 HostBinding 添加到 Linker 中,然後調用 linker.linkAll() 進行全部 Binding 的依賴關聯。

4.2.7 BindingsGroup.java

內部主要一個 LinkedHashMap 變量,key 爲需要需要依賴注入的類類全名,value 爲其對應的 Binding 對象。

4.2.8 DeferredBinding.java

DeferredBinding 是 Binding 的一個子類,實際就是一個標記,在 linker.requestBinding 時候如果某個 Binding 不存在,則生成一個 DeferredBinding 添加到 toLink 隊列中,在 linker.linkRequested 如果碰到 DeferredBinding 則根據 key 獲得真正的 Binding 添加到 toLink 隊列中。

4.2.9 Keys.java

這是個 Key 相關的工具類。 getMembersKey(Class<?> key) 用於返回以 "members/" + keyClassName 的字符串。

(1). boxIfPrimitive(Type type) 函數用於將原始類型轉換爲複雜類型

// TODO 其他函數作用

4.2.10 Memoizer.java

一個小的緩存抽象類,內部主要是一個用於存儲數據的 HashMap 屬性和兩個讀寫重入鎖。
Memoizer 主要函數有:

(1). create(K key)

需要子類實現的抽象函數,表示創建 Value 的方式。

(2). get(K key)

表示根據 key 從緩存中得到 value,value 如果已經存在則直接返回,否則調用 create(K key) 函數新建 value,存入緩存並返回。
Memoizer 主要用在 Loader 中,Loder 中包含一個嵌套的 Memoizer 變量,內外分別作爲類和 ClassLoader 的緩存。

4.2.11 ModuleAdapter.java

抽象類,APT 會爲每一個被 @Module 修飾的類自動生成一個繼承自這個 ModuleAdapter 的子類。該子類會以 ModuleClass 的 ClassName 加上 $$ModuleAdapter 命名,在 ModuleClass 的同一個 package 下。
ModuleAdapter 主要屬性有:

(1). Class moduleClass

表示 ModuleAdapter 對應的 ModuleClass。

(2). injectableTypes

String 數組,存儲需要依賴注入的類類名。爲 @Module 註解的 injects 屬性值。

(3). staticInjections

Class 數組,存儲有靜態屬性依賴需要注入的類。

(4). boolean overrides

表示某個 Module 的 @Provides 函數可以覆蓋其他 Module,建議只在測試以及開發模式使用。

(5). includes

表示 Module 由哪些其他類組成。

(6). boolean complete

表示這個 Module 需要的所有 Binding 是否可以互相提供依賴,即是否能組成一個完整的 DAG。True 表示可以,False 表示不可以。如果一個 Module 有外部依賴的 Bindings 則爲 False。

(7). boolean library

表示這個 Module 是否提供對外的DenpendencyBinding,True 表示是,False 表示所有 Binding 僅被自己用到。

4.2.12 Modules.java

Modules.java 對外只有一個靜態的 loadModules 函數,作用是返回一組 Module 類所對應的一組 ModuleAdapter 實例。
該函數入參爲 Loader 和一個 ModuleClass 對象數組 seedModulesOrClasses,函數返回一個 HashMap,key 爲 ModuleAdapter 對象,Value 爲類似入參的 ModuleClass 對象,返回結果不僅包含入參 ModuleClass 及其對應的ModuleAdapter,也包含入參 ModuleClass 嵌套的 ModuleClass 及其對應的ModuleAdapter。

loadModules 的邏輯比較簡單,先通過 Loader.getModuleAdapter() 函數依次得到入參 seedModulesOrClasses 對應的 ModuleAdapter,然後查找得到的 ModuleAdapter 嵌套的 ModuleClass 對應的 ModuleAdapter,ModuleAdapter 嵌套的 ModuleClass 都存放在 ModuleAdapter 的 includes 對象中,由 APT 在編譯時解析生成。

4.2.13 ProblemDetector.java

Binding 問題檢測。 ProblemDetector 主要函數有:

(1). detectCircularDependencies(Collection bindings)

檢測一組 Binding 是否存在循環依賴。

(2). detectUnusedBinding(Collection bindings)

檢測一組 Binding 中是否存在無用的 Binding,即既不依賴其他 Binding 也不被其他 Binding 依賴,對於 DAG 圖來說就是孤立的節點。

(3). detectProblems(Collection values)

檢測一組 Binding 是否存在問題,直接調用上面兩個函數檢測。這個函數會被 DaggerObjectGraph.validate() 調用進行檢測。

4.2.14 BuiltInBinding.java

ProvidesBinding 是 Binding 的子類,它的作用是在 attach 時就已經得到了最終的 Binding,get() 調用時直接返回即可。

4.2.15 LazyBinding.java

LazyBinding 是 Binding 的子類,它的作用是延遲實例化 Binding,調用它的 get() 函數時只是返回一個 Lazy 匿名內部類對象,只有調用這個對象的 get() 函數時纔會 返回真正的 Dependency。

這樣做的一個好處就是如果,真正的 Binding 的生成很耗費性能,則可以在最開始時只生成輕量級的 LazyBinding,真正要使用時才初始化真正的 Binding。

4.2.16 ProvidesBinding.java

ProvidesBinding 是 Binding 的子類,對於 Provide 方式的注入,APT 會一個繼承自 ProvidesBinding.java 的子類,該生成類以 Provide 函數名首字母大寫加上 ProvidesAdapter 命名,是 Provide 函數所在 Module 對應生成的ModuleAdapter中的靜態內部類。
ProvidesBinding 主要屬性有:

(1). moduleClass

表示被 @Provides 修飾的函數所在的 Module 類名。

(2). methodName

表示被 @Provides 修飾的函數函數名。

4.2.17 SetBinding.java

SetBinding 是 Binding 的子類,它的不同處在於保存了父 Binding,這樣就形成了一個鏈表。

4.2.18 StaticInjection.java

抽象類,APT 會爲每一個被 @Inject 修飾的靜態屬性自動生成一個繼承自這個 StaticInjection 的子類。該子類會以屬性類的 ClassName 加上 $$StaticInjection 命名,在屬性類的同一個 package 下。

4.2.19 Lazy.java

Lazy 是一個接口,用來標記表示可以通過 get() 函數得到真正的對象。

4.2.20 MembersInjector.java

MembersInjector 是一個接口,提供了 injectMembers() 用來向Host對象中注入(即設置)DependencyHostDependency需要實現此接口。

4.2.21 Module.java

Module 是一個運行時註解,可以用來修飾類、接口、Enum。用來爲 Dagger 提供需要依賴注入的 Host 信息及一些 Dependency 的生成方式。Module 的屬性都在ModuleAdapter.java中介紹過,唯一沒有介紹過的 addsTo 表示 Module 可以作爲哪些類的依賴。

被 @Module 註解修飾類(ModuleClass),APT 會生成一個以 ModuleClass 的 ClassName 加上 $$ModuleAdapter 命名,在 ModuleClass 的同一個 package 下的子類。

4.2.22 Provides.java

Provides 是一個註解,只可以用來修飾函數。
每個被 @Provides 修飾的生成函數都會生成一個繼承自 ProvidesBinding.java 的子類,ProvidesBinding.java 繼承自 Binding.java,生成類以 Provide 函數名首字母大寫加上 ProvidesAdapter 命名,是 Provide 函數所在 Module 對應生成的ModuleAdapter中的靜態內部類。 Binding 更具體信息在下面會介紹。

4.2.23 ErrorHandler Interface

位於 Linker.java 內部,表示處理 Linker.linkRequested() 運行過程中的 error。

4.2.24 ThrowingErrorHandler.java

上面 ErrorHandler Interface 的實現類,將 errors 彙總後以 IllegalStateException 拋出,爲 Linker.linkRequested() 運行過程中積累的 errors 的默認處理方式。

5. 聊聊 Dagger 本身

Dagger 由於其自身的複雜性,其實是一個上手難度頗高的庫,難學會、難用好。但從功能上來講,它又是一個實用價值非常高的庫。而且即將發佈的 Dagger 2.0 已經被 Square 轉手交給了 Google 來開發和維護,從今以後它就是 Google 的官方庫了,那麼不論從官方支持方面還是從流行度上面, Dagger 都將會有一個很大的提升。關於 Dagger 的功能和用法,我會寫一篇文章詳細講述。在本文的最後,列兩個可能比較多人會問的問題和簡單的回答:

(1). Dagger 適合什麼樣的項目

Dagger 是一個依賴注入庫,而依賴注入是一種優秀的編程思想,它可以通過解耦項目來提升項目的可閱讀性、可擴展性和可維護性,並使得單元測試更爲方便。因此,Dagger 適用於所有項目

(2). Dagger 適合什麼樣的個人和團隊

Dagger 適合有學習能力並且願意學習的個人和團隊。這裏要注意,如果你是開發團隊的負責人,在決定啓用 Dagger 之前一定要確認你的所有隊員(起碼是大部分隊員)都符合這樣的條件,否則 Dagger 可能會起反作用,畢竟——它不是 ButterKnife。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章