7.Dagger2 入門

一、首先你要知道什么是依賴?

想要理解Dagger2,首先你要理解一個概念,就是什么是依賴,懂的同學可以省過此段。這里給大家舉個通俗易懂的例子,讓你秒懂什么是依賴,你今天去辦港澳通行證,出入境告訴你想辦理這個證你要攜帶你的個人身份證和戶口本,不然不給你辦,這個時候就產生了一個依賴關系,通行證的辦理需要依賴身份證和戶口本,我們就想到用代碼實現可以如下。

 class PassPort{  

private IDCardBean mIDCard;  
private FamilyRegisterBean mFamilyRegister;  

public PassPort(){  
this.mIDCard = new IDCardBean();  
this.mFamilyRegister = new FamilyRegisterBean();  
}  
 }  

或者

class PassPort{  
private IDCardBean mIDCard;  
private FamilyRegisterBean mFamilyRegister;  

public PassPort(IDCardBean mIDCard, FamilyRegisterBean mFamilyRegister){  
this.mIDCard = mIDCard;  
this.mFamilyRegister = mFamilyRegister;  
 }  
}  

IDCardBean mCard = new IDCardBean();  
FamilyRegisterBean mFR = new FamilyRegisterBean();  

 PassPort mPassPort = new PassPort(mCard, mFR);  
}  

上面兩段代碼咋一看沒啥毛病,但是如果現在我告訴你,你去辦理身份證也同時需要帶戶口本還有身份證照片回執等,辦理戶口本還需要各種證明等,那么這個時候 IDCardBean 和 FamilyRegisterBean 的構造函數是不是也要相應地做出改變,而通行證 PassPort 的代碼是不是也要做出修改,這時就有人說了,沒事啊我可以改,那么如果你項目中有一百個地方都用到了呢,你是不是都要逐個逐個去改呢?為了不需要改動太多地方,我們必須給通行證和身份證、戶口本進行降耦。

二、知道依賴的概念后,你要知道Dagger2是個什么東西

從上面的例子可以看出,如果按照我們日常的編碼習慣,如果存在依賴關系的某一處有一些小修改,都可能會給代碼量大的項目造成很大的改動,這樣是不符合工程思想的,所以就出現了各種依賴注入框架,Dagger2就是其中的一個,它的前身是Dagger1,Dagger1是由Square公司開發的,而Dagger2卻是由谷歌公司接手開發,兩者還是存在一些區別的,不過這篇文章不會做太多介紹,主要還是教會大家如何使用Dagger2。

三、初步了解Dagger2后,你需要知道幾個重要的概念

在正式使用Dagger2之前,你需要了解以下幾個重要的注解,為了方便大家的理解,我會用最通俗的話來解釋,如果想知道更多的可以查看官方文檔。

1、@Inject:目標類用這個注解修飾的一些方法或者屬性(字段),說明這個目標類需要這個依賴,比如上面例子中就可以在身份證字段和戶口本字段上加上這個注解

2、@Module:用這個注解修飾的類說明是一個提供依賴的類,比如我們可以把通行證的依賴身份證和戶口本一起放在用這個注解修飾的類中

3、@Provide:這個注解主要用來修飾 @Module 修飾的類中的方法

4、@Component:這個是把 @Inject 和 @Module 關聯在一起的橋梁

5、@Scope:作用域

四、你需要知道Dagger2的使用步驟

1、定義所需要的bean,比如前面的 PassPort 、IDCardBean 之類的,然后在需要注入依賴的地方標上 @Inject 注解。

2、定義一個類,命名為 XXXModule ,比如 PassPortModule,用 @Module 注解這個類。

3、在PassPortModule 這個類中定義你需要提供依賴的方法,這些方法名一般為 provideXXX,而且要用 @Provides 來注解,方法體就是 new 一個依賴實例,比如 new 一個 IDCardBean 實例。

4、創建一個接口,一般命名為 XXXComponent,接口要用 @Component 注解,并需要傳入相應的 module 參數,例如 @Component(modules = PassPortModule.class),另外需要提供一個注入方法,一般寫成 void inject(目標類 tmp),比如 void inject(PassPort passPort)

五、開始上代碼

1、在根項目(project)的 build.gradle 中添加以下代碼,為了添加 apt 插件

dependencies {
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
}

2、在 app (module)的 build.gradle 中添加以下代碼

dependencies {
apt 'com.google.dagger:dagger-compiler:2.6'
compile 'com.google.dagger:dagger:2.6'
}

注意上面兩個步驟的版本可能會有所不一樣。

3、定義PassPortActivity,并在需要依賴的屬性上用 @Inject 注解

 public class PassPortActivity extends BaseActivity{  

@Inject  
IDCardBean mIDCard;  

@Inject  
FamilyRegisterBean mFamilyRegister;  

@Override  
protected void onCreate(Bundle savedInstanceState) {  
super.onCreate(savedInstanceState);  
setContentView(getContentViewId());  
}  
}  

4、定義PassPortModule,并用 @Module 注解這個類,類里面寫上提供依賴的方法,方法用@Provides注解,方法體則是 new 出依賴實例

@Module  
public class PassPortModule {  
private final PassPortActivity mView;  

public PassPortModule(PassPortActivity view) {  
this.mView = view;  
}  

@PerActivity  
@Provides  
 public IDCardBean provideIDCard() {  
return new IDCardBean(mView);  
 }  

 @PerActivity  
@Provides  
 public FamilyRegisterBean provideFamilyRegister() {  
return new FamilyRegisterBean(mView);  
}  
}  

5、定義PassPortComponent

 @PerActivity  
 @Component(dependencies = ApplicationComponent.class, modules = PassPortModule.class)  
 public interface PassPortComponent {   
  void inject(PassPortActivity activity);  
 }  

6、這個時候依賴關系就已經建立好了,你需要重新編譯一下你的工程,Android studio 的快捷鍵是 ctrl+f9

7、在你 onCreate 方法里面添加以下代碼

  DaggerPassPortComponent.builder()  
 .applicationComponent(getAppComponent())  
 .passPortModule(new PassPortModule(this))  
.build()  
.inject(this);
來自:https://blog.csdn.net/interface_l/article/details/69946685#

Dagger-匕首,鼎鼎大名的Square公司旗下又一把利刃(沒錯!還有一把黃油刀,喚作ButterKnife);故此給本篇取名神兵利器Dagger2。

Dagger2起源于Dagger,是一款基于Java注解來實現的完全在編譯階段完成依賴注入的開源庫,主要用于模塊間解耦、提高代碼的健壯性和可維護性。Dagger2在編譯階段通過apt利用Java注解自動生成Java代碼,然后結合手寫的代碼來自動幫我們完成依賴注入的工作。

起初Square公司受到Guice的啟發而開發了Dagger,但是Dagger這種半靜態半運行時的框架還是有些性能問題(雖說依賴注入是完全靜態的,但是其有向無環圖(Directed Acyclic Graph)還是基于反射來生成的,這無論在大型的服務端應用還是在Android應用上都不是最優方案)。因此Google工程師Fork了Dagger項目,對它進行了改造。于是變演變出了今天我們要討論的Dagger2,所以說Dagger2其實就是高配版的Dagger。

依賴注入(Dependency Injection)

那么什么是依賴注入呢?在解釋這個概念前我們先看一小段代碼:

public class Car{

    private Engine engine;

    public Car(){
        engine = new Engine();
    }
}

這段Java代碼中Car類持有了對Engine實例的引用,我們稱之為Car類對Engine類有一個依賴。而依賴注入則是指通過注入的方式實現類與類之間的依賴,下面是常見的三種依賴注入的方式:

1、構造注入:通過構造函數傳參給依賴的成員變量賦值,從而實現注入。

public class Car{

    private Engine engine;

    public Car(Engine engine){
        this.engine = engine;
    }
}

2、接口注入:實現接口方法,同樣以傳參的方式實現注入。

public interface Injection<T>{

    void inject(T t);
}

public class Car implements Injection<Engine>{

    private Engine engine;

    public Car(){}

    public void inject(Engine engine){
        this.engine = engine;
    }

}

3、注解注入:使用Java注解在編譯階段生成代碼實現注入或者是在運行階段通過反射實現注入。

public class Car{

    @Inject
    Engine engine;

    public Car(){}
}

前兩種注入方式需要我們編寫大量的模板代碼,而機智的Dagger2則是通過Java注解在編譯期來實現依賴注入的。

為什么需要依賴注入

我們之所是要依賴注入,最重要的就是為了解耦,達到高內聚低耦合的目的,保證代碼的健壯性、靈活性和可維護性。

下面我們看看同一個業務的兩種實現方案:

1、方案A

public class Car{

    private Engine engine;
    private List<Wheel> wheels;

    public Car(){
        engine = new Engine();
        wheels = new ArrayList<>();
        for(int i = 0; i < 4; i++){
            wheels.add(new Wheel());
        }
    }

    public void start{
        System.out.println("啟動汽車");
    }
}

public class CarTest{

    public static void main(String[] args){
        Car car = new Car();
        car.start();
    }
} 

2、方案B

public class Car{

    private Engine engine;
    private List<Wheel> wheels;

    public Car(Engine engine, List<Wheel> wheels){
        this.engine = engine;
        this.wheels = wheels;
    }

    public void start{
        System.out.println("啟動汽車");
    }
}

public class CarTest{

    public static void main(String[] args){

        Engine engine = new Engine();
        List<Wheel> wheels = new ArrayList<>();
        for(int i = 0; i < 4; i++){
            wheels.add(new Wheel());
        }
        Car car = new Car(engine, wheels);
        car.start();
    }
}

方案A:由于沒有依賴注入,因此需要我們自己是在Car的構造函數中創建Engine和Wheel對象。

方案B:我們手動以構造函數的方式注入依賴,將engine和wheels作為參數傳入而不是在Car的構造函數中去顯示的創建。

方案A明顯喪失了靈活性,一切依賴都是在Car類的內部創建,Car與Engine和Wheel嚴重耦合。一旦Engine或者Wheel的創建方式發生了改變,我們就必須要去修改Car類的構造函數(比如說現在創建Wheel實例的構造函數改變了,需要傳入Rubber(橡膠)了);另外我們也沒辦法替換動態的替換依賴實例(比如我們想把Car的Wheel(輪胎)從鄧祿普(輪胎品牌)換成米其林(輪胎品牌)的)。這類問題在大型的商業項目中則更加嚴重,往往A依賴B、B依賴C、C依賴D、D依賴E;一旦稍有改動便牽一發而動全身,想想都可怕!而依賴注入則很好的幫我們解決了這一問題。

為什么是Dagger2

無論是構造函數注入還是接口注入,都避免不了要編寫大量的模板代碼。機智的猿猿們當然不開心做這些重復性的工作,于是各種依賴注入框架應用而生。但是這么多的依賴注入框架為什么我們卻偏愛Dagger2呢?我們先從Spring中的控制反轉(IOC)說起。

談起依賴注入,做過J2EE開發的同學一定會想起Spring IOC,那通過迷之XML來配置依賴的方式真的很讓人討厭;而且XML與Java代碼分離也導致代碼鏈難以追蹤。之后更加先進的Guice(Android端也有個RoboGuice)出現了,我們不再需要通過XML來配置依賴,但其運行時實現注入的方式讓我們在追蹤和定位錯誤的時候卻又萬分痛苦。開篇提到過Dagger就是受Guice的啟發而開發出來的;Dagger繼承了前輩的思想,在性能又碾壓了它的前輩Guice,可謂是長江后浪推前浪,前浪死在沙灘上。

又如開篇我在簡介中說到的,Dagger是一種半靜態半運行時的DI框架,雖說依賴注入是完全靜態的,但是生成有向無環圖(DAG)還是基于反射來實現,這無論在大型的服務端應用還是在Android應用上都不是最優方案。升級版的Dagger2解決了這一問題,從半靜態變為完全靜態,從Map式的API變成申明式API(@Module),生成的代碼更優雅高效;而且一旦出錯我們在編譯期間就能發現。所以Dagger2對開發者的更加友好了,當然Dagger2也因此喪失了一些靈活性,但總體來說利還是遠遠大于弊的。

前面提到這種A B C D E連續依賴的問題,一旦E的創建方式發生了改變就會引發連鎖反應,可能會導致A B C D都需要做針對性的修改;但是騷年,你以為為這僅僅是工作量的問題嗎?更可怕的是我們創建A時需要按順序先創建E D C B四個對象,而且必須保證順序上是正確的。Dagger2就很好的解決了這一問題(不只是Dagger2,在其他DI框架中開發者同樣不需要關注這些問題)。

Dagger2注解

開篇我們就提到Dagger2是基于Java注解來實現依賴注入的,那么在正式使用之前我們需要先了解下Dagger2中的注解。Dagger2使用過程中我們通常接觸到的注解主要包括:@Inject, @Module, @Provides, @Component, @Qulifier, @Scope, @Singleten。

  • @Inject:@Inject有兩個作用,一是用來標記需要依賴的變量,以此告訴Dagger2為它提供依賴;二是用來標記構造函數,Dagger2通過@Inject注解可以在需要這個類實例的時候來找到這個構造函數并把相關實例構造出來,以此來為被@Inject標記了的變量提供依賴;

  • @Module:@Module用于標注提供依賴的類。你可能會有點困惑,上面不是提到用@Inject標記構造函數就可以提供依賴了么,為什么還需要@Module?很多時候我們需要提供依賴的構造函數是第三方庫的,我們沒法給它加上@Inject注解,又比如說提供以來的構造函數是帶參數的,如果我們之所簡單的使用@Inject標記它,那么他的參數又怎么來呢?@Module正是幫我們解決這些問題的。

  • @Provides:@Provides用于標注Module所標注的類中的方法,該方法在需要提供依賴時被調用,從而把預先提供好的對象當做依賴給標注了@Inject的變量賦值;

  • @Component:@Component用于標注接口,是依賴需求方和依賴提供方之間的橋梁。被Component標注的接口在編譯時會生成該接口的實現類(如果@Component標注的接口為CarComponent,則編譯期生成的實現類為DaggerCarComponent),我們通過調用這個實現類的方法完成注入;

  • @Qulifier:@Qulifier用于自定義注解,也就是說@Qulifier就如同Java提供的幾種基本元注解一樣用來標記注解類。我們在使用@Module來標注提供依賴的方法時,方法名我們是可以隨便定義的(雖然我們定義方法名一般以provide開頭,但這并不是強制的,只是為了增加可讀性而已)。那么Dagger2怎么知道這個方法是為誰提供依賴呢?答案就是返回值的類型,Dagger2根據返回值的類型來決定為哪個被@Inject標記了的變量賦值。但是問題來了,一旦有多個一樣的返回類型Dagger2就懵逼了。@Qulifier的存在正式為了解決這個問題,我們使用@Qulifier來定義自己的注解,然后通過自定義的注解去標注提供依賴的方法和依賴需求方(也就是被@Inject標注的變量),這樣Dagger2就知道為誰提供依賴了。----一個更為精簡的定義:當類型不足以鑒別一個依賴的時候,我們就可以使用這個注解標示;

  • @Scope:@Scope同樣用于自定義注解,我能可以通過@Scope自定義的注解來限定注解作用域,實現局部的單例;

  • @Singleton:@Singleton其實就是一個通過@Scope定義的注解,我們一般通過它來實現全局單例。但實際上它并不能提前全局單例,是否能提供全局單例還要取決于對應的Component是否為一個全局對象。

我們提到@Inject和@Module都可以提供依賴,那如果我們即在構造函數上通過標記@Inject提供依賴,有通過@Module提供依賴Dagger2會如何選擇呢?具體規則如下:

  • 步驟1:首先查找@Module標注的類中是否存在提供依賴的方法。

  • 步驟2:若存在提供依賴的方法,查看該方法是否存在參數。

    • a:若存在參數,則按從步驟1開始依次初始化每個參數;

    • b:若不存在,則直接初始化該類實例,完成一次依賴注入。

  • 步驟3:若不存在提供依賴的方法,則查找@Inject標注的構造函數,看構造函數是否存在參數。

    • a:若存在參數,則從步驟1開始依次初始化每一個參數

    • b:若不存在,則直接初始化該類實例,完成一次依賴注入。

Dagger2使用入門

前面長篇大論的基本都在介紹概念,下面我們看看Dagger2的基本應用。關于Dagger2的依賴配置就不在這里占用篇幅去描述了,大家可以到它的github主頁下去查看官方教程https://github.com/google/dagger。接下來我們還是拿前面的Car和Engine來舉例。

1、案例A

Car類是需求依賴方,依賴了Engine類;因此我們需要在類變量Engine上添加@Inject來告訴Dagger2來為自己提供依賴。

public class Car {

    @Inject
    Engine engine;

    public Car() {
        DaggerCarComponent.builder().build().inject(this);
    }

    public Engine getEngine() {
        return this.engine;
    }
}

Engine類是依賴提供方,因此我們需要在它的構造函數上添加@Inject

public class Engine {

    @Inject
    Engine(){}

    public void run(){
        System.out.println("引擎轉起來了~~~");
    }
}

接下來我們需要創建一個用@Component標注的接口CarComponent,這個CarComponent其實就是一個注入器,這里用來將Engine注入到Car中。

@Component
public interface CarComponent {
    void inject(Car car);
}

完成這些之后我們需要Build下項目,讓Dagger2幫我們生成相關的Java類。接著我們就可以在Car的構造函數中調用Dagger2生成的DaggerCarComponent來實現注入(這其實在前面Car類的代碼中已經有了體現)

public Car() {
    DaggerCarComponent.builder().build().inject(this);
}

2、案例B

如果創建Engine的構造函數是帶參數的呢?比如說制造一臺引擎是需要齒輪(Gear)的。或者Eggine類是我們無法修改的呢?這時候就需要@Module和@Provide上場了。

同樣我們需要在Car類的成員變量Engine上加上@Inject表示自己需要Dagger2為自己提供依賴;Engine類的構造函數上的@Inject也需要去掉,應為現在不需要通過構造函數上的@Inject來提供依賴了。

public class Car {

    @Inject
    Engine engine;

    public Car() {
        DaggerCarComponent.builder().markCarModule(new MarkCarModule())
                .build().inject(this);
    }

    public Engine getEngine() {
        return this.engine;
    }
}

接著我們需要一個Module類來生成依賴對象。前面介紹的@Module就是用來標準這個類的,而@Provide則是用來標注具體提供依賴對象的方法(這里有個不成文的規定,被@Provide標注的方法命名我們一般以provide開頭,這并不是強制的但有益于提升代碼的可讀性)。

@Module
public class MarkCarModule {

    public MarkCarModule(){ }

    @Provides Engine provideEngine(){
        return new Engine("gear");
    }
}

接下來我們還需要對CarComponent進行一點點修改,之前的@Component注解是不帶參數的,現在我們需要加上modules = {MarkCarModule.class},用來告訴Dagger2提供依賴的是MarkCarModule這個類。

@Component(modules = {MarkCarModule.class})
public interface CarComponent {
    void inject(Car car);
}

Car類的構造函數我們也需要修改,相比之前多了個markCarModule(new MarkCarModule())方法,這就相當于告訴了注入器DaggerCarComponentMarkCarModule提供的依賴注入到了Car類中。

public Car() {
   DaggerCarComponent.builder()
           .markCarModule(new MarkCarModule())
           .build().inject(this);
}

這樣一個最最基本的依賴注入就完成了,接下來我們測試下我們的代碼。

public static void main(String[] args){
    Car car = new Car();
    car.getEngine().run();
}

輸出

引擎轉起來了~~~

3、案例C

那么如果一臺汽車有兩個引擎(也就是說Car類中有兩個Engine變量)怎么辦呢?沒關系,我們還有@Qulifier!首先我們需要使用Qulifier定義兩個注解:

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierA { }
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface QualifierB { }

同時我們需要對依賴提供方做出修改

@Module
public class MarkCarModule {

    public MarkCarModule(){ }

    @QualifierA
    @Provides
    Engine provideEngineA(){
        return new Engine("gearA");
    }

    @QualifierB
    @Provides
    Engine provideEngineB(){
        return new Engine("gearB");
    }
}

接下來依賴需求方Car類同樣需要修改

public class Car {

    @QualifierA @Inject Engine engineA;
    @QualifierB @Inject Engine engineB;

    public Car() {
        DaggerCarComponent.builder().markCarModule(new MarkCarModule())
                .build().inject(this);
    }

    public Engine getEngineA() {
        return this.engineA;
    }

    public Engine getEngineB() {
        return this.engineB;
    }
}

最后我們再對Engine類做些調整方便測試

public class Engine {

    private String gear;

    public Engine(String gear){
        this.gear = gear;
    }

    public void printGearName(){
        System.out.println("GearName:" + gear);
    }
}

測試代碼

public static void main(String[] args) {
    Car car = new Car();
    car.getEngineA().printGearName();
    car.getEngineB().printGearName();
}

執行結果:

GearName:gearA
GearName:gearB

4、案例D

接下來我們看看@Scope是如何限定作用域,實現局部單例的。

首先我們需要通過@Scope定義一個CarScope注解:

@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface CarScope {
}

接著我們需要用這個@CarScope去標記依賴提供方MarkCarModule。

@Module
public class MarkCarModule {

    public MarkCarModule() {
    }

    @Provides
    @CarScope
    Engine provideEngine() {
        return new Engine("gear");
    }
}

同時還需要使用@Scope去標注注入器Compoent

@CarScope
@Component(modules = {MarkCarModule.class})
public interface CarComponent {
    void inject(Car car);
}

為了便于測試我們對Car和Engine類做了一些改造:

public class Car {

    @Inject Engine engineA;
    @Inject Engine engineB;

    public Car() {
        DaggerCarComponent.builder()
                .markCarModule(new MarkCarModule())
                .build().inject(this);
    }
}
public class Engine {

    private String gear;

    public Engine(String gear){
        System.out.println("Create Engine");
        this.gear = gear;
    }
}

如果我們不適用@Scope,上面的代碼會實例化兩次Engine類,因此會有兩次"Create Engine"輸出。現在我們在有@Scope的情況測試下勞動成果:

public static void main(String[] args) {
    Car car = new Car();

    System.out.println(car.engineA.hashCode());
    System.out.println(car.engineB.hashCode());
}

輸出

Create Engine

bingo!我們確實通過@Scope實現了局部的單例。

Dagger2原理分析

前面啰里啰嗦的介紹了Dagger2的基本使用,接下來我們再分析分析實現原理。這里不會分析Dagger2根據注解生成各種代碼的原理,關于Java注解以后有機會再寫一篇文章來介紹。后面主要分析的是Dagger2生成的各種類如何幫我們實現依賴注入,為了便于理解我這里選了前面相對簡單的案例B來做分析。

Dagger2編譯期生成的代碼位于build/generated/source/apt/debug/your package name/下面:
[圖片上傳失敗...(image-75dbbe-1531401791047)]

首先我們看看Dagger2依據依賴提供方MarkCarModule生成的對應工廠類MarkCarModule_ProvideEngineFactory。為了方便大家理解對比,后面我一律會把自己寫的類和Dagger2生成的類一并放出來。

/**
* 我們自己的類
*/
@Module
public class MarkCarModule {

    public MarkCarModule(){ }

    @Provides Engine provideEngine(){
        return new Engine("gear");
    }
}

/**
* Dagger2生成的工廠類
*/
public final class MarkCarModule_ProvideEngineFactory implements Factory<Engine> {
  private final MarkCarModule module;

  public MarkCarModule_ProvideEngineFactory(MarkCarModule module) {
    assert module != null;
    this.module = module;
  }

  @Override
  public Engine get() {
    return Preconditions.checkNotNull(
        module.provideEngine(), "Cannot return null from a non-@Nullable @Provides method");
  }

  public static Factory<Engine> create(MarkCarModule module) {
    return new MarkCarModule_ProvideEngineFactory(module);
  }

  /** Proxies {@link MarkCarModule#provideEngine()}. */
  public static Engine proxyProvideEngine(MarkCarModule instance) {
    return instance.provideEngine();
  }
}

我們可以看到MarkCarModule_ProvideEngineFactory中的get()調用了MarkCarModuleprovideEngine()方法來獲取我們需要的依賴EngineMarkCarModule_ProvideEngineFactory的實例化有crate()創建,并且MarkCarModule的實例也是通過create()方法傳進來的。那么這個create()一定會在哪里調用的,我們接著往下看。

前面提到@Component是依賴提供方(MarkCarModule)和依賴需求方(Car)之前的橋梁,那我看看Dagger2是如何通過CarComponent將兩者聯系起來的。

/**
* 我們自己的類
*/
@Component(modules = {MarkCarModule.class})
public interface CarComponent {

    void inject(Car car);
}

/**
* Dagger2生成的CarComponent實現類
*/
public final class DaggerCarComponent implements CarComponent {
  private Provider<Engine> provideEngineProvider;

  private MembersInjector<Car> carMembersInjector;

  private DaggerCarComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  public static CarComponent create() {
    return builder().build();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideEngineProvider = MarkCarModule_ProvideEngineFactory.create(builder.markCarModule);

    this.carMembersInjector = Car_MembersInjector.create(provideEngineProvider);
  }

  @Override
  public void inject(Car car) {
    carMembersInjector.injectMembers(car);
  }

  public static final class Builder {
    private MarkCarModule markCarModule;

    private Builder() {}

    public CarComponent build() {
      if (markCarModule == null) {
        this.markCarModule = new MarkCarModule();
      }
      return new DaggerCarComponent(this);
    }

    public Builder markCarModule(MarkCarModule markCarModule) {
      this.markCarModule = Preconditions.checkNotNull(markCarModule);
      return this;
    }
  }
}

通過上面的代碼我們看到Dagger2依據CarComponent接口生成了實現類DaggerCarComponent(沒錯這正是我們在Car的構造函數中使用DaggerCarComponent)。DaggerCarComponent在build的時候實例化了DaggerCarComponent對象,并首先調用MarkCarModule_ProvideEngineFactory.create(builder.markCarModule)始化了provideEngineProvider變量,接著調用Car_MembersInjector.create(provideEngineProvider)初始化了carMembersInjector變量。當我們手動在Car類的構造函數中調用inject(Car car)方法時會執行carMembersInjector.injectMembers(car)。所以接下來我們要看看Car_MembersInjector的實現。

public final class Car_MembersInjector implements MembersInjector<Car> {
  private final Provider<Engine> engineProvider;

  public Car_MembersInjector(Provider<Engine> engineProvider) {
    assert engineProvider != null;
    this.engineProvider = engineProvider;
  }

  public static MembersInjector<Car> create(Provider<Engine> engineProvider) {
    return new Car_MembersInjector(engineProvider);
  }

  @Override
  public void injectMembers(Car instance) {
    if (instance == null) {
      throw new NullPointerException("Cannot inject members into a null reference");
    }
    instance.engine = engineProvider.get();
  }

  public static void injectEngine(Car instance, Provider<Engine> engineProvider) {
    instance.engine = engineProvider.get();
  }
}
來自:https://segmentfault.com/a/1190000008016507

Car_MembersInjector中的create()用于實例化自己,這個方法前面我們看到是在DaggerCarComponent中調用的。injectMembers(Car instance)engineProvider.get()的返回值賦給了依賴需求方Car的engine變量,而engineProvider.get()正是本節一開始我們提到的MarkCarModule_ProvideEngineFactory中的get()方法。至此整個依賴注入的流程就完成了。更復雜的應用場景會生成更加復雜的代碼,但原理都和前面分析的大同小異。

總結

這篇文章只是通過一些簡單的例子介紹了Dagger2的相關概念及使用,實際項目中的應用遠比這里的例子要復雜。關于Dagger2在實際項目中的應用可以參照這個開源項目 https://github.com/BaronZ88/MinimalistWeather(項目采用MVP架構,其中View層和Presenter層的解耦就是通過Dagger2來實現的)。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容