Dagger2使用簡介

最近在項目中使用了 Dagger2 這個依賴注入的框架亥曹,在這里記錄一下邓了。第一次寫技術文章,不足之處請多指教媳瞪。不過真的是寫出來才發(fā)現還是有很多不懂的地方骗炉。

介紹

  • 什么是 Dagger2
    下面這段是官網的簡介:

Dagger is a fully static, compile-time dependency injection framework for both Java and Android.

翻譯過來就是Dagger2是Java 和 Android 的一個編譯時生成代碼的依賴注入框架,如果你沒有了解過依賴注入蛇受,可能會感到不理解句葵,什么是依賴注入,為什么要使用依賴注入兢仰,使用依賴注入有什么優(yōu)點乍丈,下面來簡單的說一下。

前置知識

  • 什么是依賴注入 Dependency Injection

我們在實現一個功能的時候把将,往往需要創(chuàng)建很多對象轻专,舉個例子,如果要實現一個Computer察蹲,需要幾個類:MotherBoard, Cpu, Computer铭若。

在沒有依賴注入的時候,首先按順序構建每個對象递览,先 new 一個 Cpu叼屠,然后 new 一個 MotherBoard,再用這兩個對象 new 一個 Computer绞铃,當邏輯簡單的時候镜雨,這不會有什么問題,但是實際開發(fā)中要構建的對象要遠遠比這要復雜的多儿捧,如果某個人修改了其中一個類的 Constructor荚坞,就要修改每個使用 Constructor 的地方,如果你的創(chuàng)建所有對象的代碼有成百上千行菲盾,那就很麻煩了颓影。

使用依賴注入后,通過依賴注入的框架創(chuàng)建這些對象懒鉴,不需要自己去維護依賴關系诡挂,你只需要去維護你一個依賴的配置,這就要簡單的多了临谱。依賴注入的另一個好處璃俗,是對單元測試很友好,因為每個對象的創(chuàng)建并不依賴于特定的邏輯悉默,在寫單元測試的時候城豁,某些類的功能并不需要,或者無法在測試場景使用抄课,那么只需要替換掉就可以了唱星。在開發(fā)過程中雳旅,使用依賴注入也對多人并行開發(fā)有一定的好處,如上面的例子间聊,可以一個人開發(fā) MotherBoard岭辣,一個人開發(fā) Cpu,一個人開發(fā) Computer甸饱,每個人都不需要關心其他人的對象要如何創(chuàng)建沦童,因為每個對象的創(chuàng)建都是由依賴注入框架去維護的。

說到依賴注入的同時叹话,一定會提起控制反轉(IOC)偷遗,控制反轉是一種設計思路,意思就是說驼壶,原本我要用什么氏豌,是我自己創(chuàng)建,自己用热凹,現在是我要用什么泵喘,我就管別人要,別人給什么用什么般妙。這就又不得不提到一個軟件設計中的重要思想纪铺,依賴倒置原則

那么什么是依賴倒置原則碟渺?

舉個例子鲜锚,我們設計一臺電腦,先設計 cpu苫拍,再設計主板芜繁,最后設計機箱,這就存在一個依賴關系绒极,主板依賴于 cpu的尺寸骏令,機箱依賴于主板的尺寸,看起來是沒什么問題垄提,現在我們要改一下設計榔袋,把 cpu 的尺寸做小一圈,這就蛋疼了塔淤,因為主板依賴于 cpu 的尺寸暮蹂,主板需要重新設計烦周,機箱依賴主板的尺寸抱怔,機箱也要重新設計琴拧,整個的設計都因此而發(fā)生了變化氢哮。

現在我們換一種思路缓待,首先設計電腦整體的模型编整,據此設計機箱的結構缰儿,再根據機箱的結構設計主板,根據主板的結構去設計 cpu露泊,現在要對 cpu 的進行修改喉镰,就只需要修改 cpu 自己就行了,并不會出現上面那種全部推倒重來的現象惭笑。

這就是依賴倒置原則侣姆,將原本由下至上的依賴關系翻轉過來,變?yōu)橛猩现料鲁霖蠈記Q定下層功能捺宗,下層的修改對上層無任何影響,避免出現牽一發(fā)而動全身的現象川蒙。此時蚜厉,上層與下層的依賴是依賴抽象,而非依賴實現畜眨,這種方式能對上下層之間進行解耦昼牛,也能達到最大程度的復用】的簦控制反轉即是依賴倒置原則的一種設計思路贰健,而依賴注入則是這種思路的實現方式。

關于如何設計抽象關系恬汁,通常應當遵循面向軟件設計的5大原則(SOLID)霎烙,依賴倒置原則也是其中之一,這里就不多說了蕊连,貼一下 wiki 上的描述把悬垃,有興趣的話可以多了解一些。

S

Single responsibility principle
a class should have only a single responsibility (i.e. changes to only one part of the software's specification should be able to affect the specification of the class)

O

Open/closed principle
“software entities … should be open for extension, but closed for modification.”

L

Liskov substitution principle
“objects in a program should be replaceable with instances of their subtypes without altering the correctness of that program.” See also design by contract.

I

Interface segregation principle
“many client-specific interfaces are better than one general-purpose interface.”

D

Dependency inversion principle
one should “depend upon abstractions, [not] concretions.”

這里簡單的總結一下甘苍,個人理解尝蠕,軟件設計中的各種設計模式,原則载庭,歸根結底看彼,目的都是為了讓你的代碼更容易閱讀,擴展和維護囚聚,人不是機器靖榕,無法從數不清的代碼中快速找到自己需要的內容,也無法在快速滾動的代碼中定位問題顽铸,而且一切都依賴于抽象茁计,各種設計模式,原則谓松,本質上都是各種形式抽象星压,unix 中有一句話經常提到践剂,keep it simple,stupid娜膘,也是同樣的道理逊脯。依賴注入便是其中的一種實現方式。

  • 常見注入方式

Java有很多依賴注入的框架竣贪,常見的有 Spring, Guice, Dagger1, Dagger2等军洼。早期的注入方式是通過 xml 進行配置(Spring),xml注入的方式有很多缺點演怎,比如說無法進行代碼檢查匕争。在 Java5 出了之后,注解提供了另一個更便捷的方式颤枪,Spring 和 Guice 等框架支持了注解的方式汗捡,在JSR 330標準出現之后,更是有了統(tǒng)一的官方標準畏纲,使用注解進行注入的好處是配置不容易出錯扇住,因為代碼能編譯過,就說明寫的沒問題盗胀,但是此類注入都是通過修改編譯后生成的字節(jié)碼艘蹋,或通過 JDK 提供的動態(tài)代理(Proxy),反射等方式實現票灰,修改后的字節(jié)碼無法進行 debug女阀,運行時出錯很難排查。

Dagger2提供了另一種選擇屑迂,與其他依賴注入框架不同的是浸策,Dagger2的注入方式是在編譯時生成代碼,通過 JDK 提供的 Annotation Processor Tool功能惹盼,在移動終端這種性能有限的環(huán)境下庸汗,生成代碼的方式更合適,性能也更好手报。但是也存在一些局限性蚯舱,例如不支持任何動態(tài)注入,因為代碼在編譯后就已經固定了掩蛤,會增加編譯時間等枉昏。

以上內容參考了這個視頻,有興趣的話可以看一下)

  • Annotation Processor

上面提到了注解處理器揍鸟,這里簡單的說一下兄裂,注解處理器是 Java5之后提供的一種在編譯時動態(tài)生成代碼的功能,通過讀取源碼中使用注解標記的位置,使用自己的邏輯動態(tài)生成代碼懦窘。需要注意的是前翎,生成代碼的時機是在編譯之前稚配,在 Android的基于 Gradle 的編譯環(huán)境下畅涂,每個 Module 都需要指定自己的注解處理器。

Dagger2基本使用

現在開始說具體的使用道川,Dagger2使用JSR 330標準的API午衰,和自己定義的一些注解,通過幾個簡單的注解和接口冒萄,就能進行注入操作臊岸。

首先要說明的是整體的一個思路,要記住的就是 No Magic尊流,依賴注入從根本上來說帅戒,就是創(chuàng)建一個對象并給某個變量賦值,Dagger2的本質就注解處理器在編譯時生成代碼崖技,實際上就是對你所需要的變量進行賦值逻住,而想要正確的給這個變量進行賦值,需要兩點:

  1. 要創(chuàng)建哪個對象
  2. 如何創(chuàng)建這個對象

第一點迎献,首先要找到要創(chuàng)建哪個對象瞎访,Dagger2在匹配的時候通過類型和 Qualifier 注解來定位一個對象,所以不要在寫了兩個相同類型的注入并且編譯不通過的時候感到奇怪吁恍。

第二點扒秸,所有的注入對象,框架都要知道如何去創(chuàng)建它冀瓦,以及它所依賴的其他所有對象伴奥,編譯不通過的時候,第一件事就是看報錯信息翼闽, Dagger2的報錯信息在大部分情況下都很詳細拾徙,明確的指出了到底是缺少配置,還是配置錯誤肄程,或者其他原因锣吼,框架也不能把一個對象變出來,如果你都不知道這個對象是怎么創(chuàng)建的蓝厌,框架又怎么能知道呢玄叠?

看到這里可能比較疑惑,不著急拓提,先往下看读恃。

這里有幾個關鍵性的注解:

  • @Inject
  • @Provides
  • @Module
  • @Component

先來看一個例子,最簡單的注入:

還記得上面的兩點么,第一點寺惫,告訴框架如何創(chuàng)建對象

public class MotherBoard {
    private Cpu mCpu;

    @Inject
    public MotherBoard(Cpu cpu) {
        mCpu = cpu;
    }

    ...
}

第二點疹吃,告訴框架我要什么對象

public class Computer {
    @Inject
    MotherBoard motherBoard;

    ...
}

這樣在 Computer 對象中,就能獲取到 MotherBoard 了西雀。

如果你仔細看懂了上面的內容萨驶,就會發(fā)現這段代碼并不能正確編譯,因為框架不知道如何創(chuàng)建 Cpu 對象艇肴,這里來說明另一種告訴框架如何創(chuàng)建某個對象的方式:

@Provides
public static Cpu provideCpu() {
    return new Cpu();
}

那么這段代碼要在哪里執(zhí)行呢腔呜?下面來介紹另一個重要注解@Module

在Dagger2中,所有用@Provides注解的方法再悼,都屬于一個 Module核畴,其實就是一個類上面寫@Module

@Module
class ComputerModule {
    @Provides
    static Cpu provideCpu() {
        return new Cpu();
    }

    ...
}

OK,現在完事齊全冲九,只差注入了谤草,沒有這最后一步,前面的一切都是沒用的莺奸。那么到底要怎么注入呢丑孩?現在需要另一個注解@Component
在 Dagger2 中,有一個 graph 的概念憾筏,其實每個依賴注入框架都有類似的概念嚎杨,要注入的對象,及其所有依賴的對象之間氧腰,必然存在一個完整的依賴關系圖枫浙,在注入之前,必須要能完整的構建出這個依賴關系圖古拴。Component 注解標記的類代表了一個完整的依賴關系的終點箩帚。當然這一切都是框架去做的,你所需要的就是正確的配置出這個依賴關系黄痪。

@Component(modules = {
        ComputerModule.class
})
public interface ComputerComponent {
    void inject(Computer computer);
}

編譯后紧帕,dagger2會生成一個ComputerComponent 接口的實現類,類名為 Dagger + Component 名字桅打,調用其中的方法就可以對 Computer 進行注入了是嗜。

Computer computer = new Computer();
DaggerComputerComponent.builder().build()
                .inject(computer);

如果你連 Computer 都不想自己創(chuàng)造,也可以這么寫:

// Computer
public class Computer {
    MotherBoard motherBoard;
    @Inject
    Computer(MotherBoard motherBoard) {
        this.motherBoard = motherBoard;
    }

    ...
}

// Component
public interface ComputerComponent {
    Computer make();
}

看到這里可能會有疑問挺尾,這寫 Component鹅搪,Module,Provide 的類和方法遭铺,為什么都叫這樣的名字呢丽柿?在上面注入相關的方法恢准,類名,及后面會說到的相關注入功能甫题,方法名類名均與注入過程無關馁筐,起什么名字只是為了方便閱讀,方便閱讀也是很重要的坠非。

最簡單的說完了敏沉,下面來說一下各種不同場景的使用。

你可能已經注意到了上面的DaggerComputerComponent是帶 Builder 的麻顶,那這個 Builder 是做什么的呢赦抖?

Provides 方法可以是static的舱卡,也可以不是辅肾,如果不是,那么就需要一個 Module 對象來調用這個方法轮锥,這時候Component 中的 Builder 就有用了矫钓。
修改一下就會變成這樣:

@Module
class ComputerModule {
    @Provides
    Cpu provideCpu() {
        return new Cpu();
    }
}

// Inject
DaggerComputerComponent.builder()
                .computerModule(new ComputerModule())
                .build()
                .inject(computer);

有的時候你可能想用接口來注入

public interface Computer {

}

public class MyComputer implements Computer {

    private MotherBoard mMotherBoard;

    @Inject
    MyComputer(MotherBoard motherBoard) {
        mMotherBoard = motherBoard;
    }
}

// Module
@Provides
Computer provideComputer(MyComputer myComputer) {
    return myComputer;
}

這種情況下,如果你的 Module 里都是上面那樣舍杜,也可以寫成這樣

@Module
abstract class ComputerModule {
    @Provides
    static Cpu provideCpu() {
        return new Cpu();
    }

    @Binds
    abstract Computer provideComputer(MyComputer myComputer);
}

這里使用一個新的注解@Binds新娜,這個注解和@Provides的功能是相同的,區(qū)別在于既绩,如果你的 Provide 方法入參與返回值相同概龄,僅僅是做了類型轉換,那么可以省略這個方法調用饲握,以一個 abstract 方法代替私杜,在注入時會直接使用傳入的參數,如果你去看生成的代碼救欧,就會發(fā)現實際并沒有調用你寫的這個 abstract 方法衰粹,而是直接使用了入參的變量,減少了一個方法調用也是可以節(jié)約一些性能的笆怠,在Android這種性能有限的平臺上铝耻,還是很有意義的。

Scope

只要是依賴注入蹬刷,一定會有一個 Scope 的概念瓢捉,通常代表了注入對象的作用域。在 Dagger2办成,也就代表了這個注入的對象的生命周期泡态。

一個常見的 Scope 是 @Singleton,在可注入的類的類名上诈火,或者 @Provides 標記的方法上使用這個注解兽赁,表示改對象是單例的状答。使用的時候需要注意,如果使用Scope刀崖,對應的 Component 也要使用此注解進行標記惊科。僅僅一個@Singleton 注解實際上并不能實現單例,單例的實現是依賴于 Component 的亮钦,從同一個 Component 對象進行注入馆截,才是單例,如果你創(chuàng)建了兩個 Component用來注入一個單例蜂莉,仍然會產生兩個不同的對象蜡娶,如果打開生成的代碼進行查看,會有更深刻的印象映穗。

同樣也可以自己定義 Scope

@Documented
@Retention(RUNTIME)
@CanReleaseReferences
@Scope
public @interface MyScope {}

事實上窖张,自己定義的 scope,與@Singleton 并沒有什么區(qū)別蚁滋,因為Scope 的實現都是基于Component宿接,在同一個 Component 對象中,標記了 Scope 的注入對象辕录,都只會注入同一個對象睦霎,相當于局部的單例,主要的目的是實現清晰的結構走诞。

實際使用中可能還會有這樣的場景副女,某些情況下不需要這個Component 中再保存單例對象,這時候可以將其釋放掉蚣旱,仔細看上面的自定義 Scope碑幅,有一個@CanReleaseReferences注解,使用這個注解就可以額外注入一個ReleasableReferenceManager對象來對此進行操作姻锁。h'n

// Module
@MyScope
@Provides
static Computer provideMyComputer(MotherBoard motherBoard) {
    return new MyComputer(motherBoard);
}

// Component
@Inject @ForReleasableReferences(MyScope.class)
ReleasableReferenceManager releasableReferenceManager;

ReleasableReferenceManager有兩個方法枕赵,releaseStrongReferences的作用是把保存的對象轉移到一個 WeakReference 中,眾所周知WeakReference中的對象可以被回收位隶,另一個方法restoreStrongReferences則可以把前面的 WeakReference 中的還沒有被回收的對象變回到強引用狀態(tài)拷窜。

關于ForReleasableReferences官方的例子是在內存不足時釋放對象,但是實際應用中并未發(fā)現實際的實用場景涧黄,待補充篮昧。

關于 Scope,還有一個@Reusable 注解笋妥,官方的解釋是針對某些特殊的可以隨便重復使用的對象懊昨,實際操作沒有發(fā)現什么區(qū)別,也沒有想到什么使用場景春宣,待補充酵颁。

延遲注入

有的時候注入的對象并不想在注入的時候創(chuàng)建嫉你,而是在需要的時候自己去創(chuàng)建

@Inject
Lazy<MotherBoard> motherBoard;

// Inject
motherBoard.get();

多次注入

如果需要多次獲取不同的對象,如下代碼可以獲取10次主板對象躏惋,每次的對象都是一個新的主板幽污。

Provider<MotherBoard> motherBoard;

// Inject
for (int i = 0; i < 10; i++)
    motherBoard.get();

Qualifier

前面說到Dagger2如何定位需要注入的對象,首先是通過類型簿姨,但是如果需要多個相同類型的要注入怎么辦呢距误?這時候就需要Qualifier 注解了。

在@Provides 或@Inject 的地方使用@Named 注解扁位,可以指定一個名字用來匹配要注入的對象准潭。

@Provides
@Named("MyComputer")
static Computer provideComputer() {
    return new MyConputer();
}

@Provides
@Named("HisComputer")
static Computer provideComputer() {
    return new HisConputer();
}

// Component
public interface ComputerComponent {
    @Named("MyComputer")
    Computer make();

    @Named("HisComputer")
    Computer make();
}

@Named注解使用一個字符串來給每個注入對象增加一個名字,如果在很多地方使用域仇,比如說多個 Module 中注入不同的對象刑然,就不是很好定位,可以自己定義一個用@Qualifier標記的注解殉簸,代替上面的@Named注解

@Qualifier
@Documented
@Retention(SOURCE)
public @interface MyComputer {
}

可選綁定

如果某個依賴對象允許不存在闰集,可以在 Module 里使用@BindsOptionalOf注解,這樣其他的需要依賴此對象的地方般卑,都可以寫一個 Optional<MotherBoard> ,支持 guava 和 java8的 Optiona 類爽雄,不了解 Optiona 的可以自己查一下蝠检,主要是用來解決空指針異常問題的。也同樣支持 Lazy 和 Provider挚瘟。

例如我現在設計了一種神奇的電腦可以沒有主板:

public class MotherBoard {
    private Cpu mCpu;

    MotherBoard(Cpu cpu) {
        mCpu = cpu;
    }
}

public class MyComputer implements Computer {

    private MotherBoard mMotherBoard;

    @Inject
    MyComputer(Optional<MotherBoard> motherBoard) {

        ...
    }
}

// Module
@BindsOptionalOf
abstract MotherBoard optionalMotherBoard();

現在即使不提供 MotherBoard 對象的注入叹谁,也不會報錯。
在一個 Module 中使用@BindsOptionalOf后乘盖,同樣的 Module 不允許使用@Provides 提供相同的注入對象焰檩,并且不能有使用@Inject 的 Constructor。當有多個 Module 提供不同的依賴關系時订框,@BindsOptionalOf才有意義析苫。

現在我修改了設計,又想要主板了穿扳,于是單獨寫一個MotherBoardModule用來提供主板:

@Module
class MotherBoardModule {
    @Provides
    static MotherBoard provideMotherBoard(Cpu cpu) {
        return new MotherBoard(cpu);
    }
}

又可以把主板注入到 Computer 中了衩侥。

BindsInstance

有時,Component 中需要注入的對象可能運行時才創(chuàng)建矛物,這是可以使用@BindsInstance注解茫死,給 Component 傳入一個可注入的對象。比如說現在我的電腦里的 Cpu 改為使用其他廠家的履羞,不自己生產了峦萎,那么我只需要在構建 Component 的時候作為一個參數傳進去就行了屡久。

public class Cpu {
    public Cpu() {
    }
}

// Component
@Component.Builder
    interface Builder {
        @BindsInstance
        Builder useCpu(Cpu cpu);

        ComputerComponent build();
    }

// Inject
ComputerComponent computerComponent = DaggerComputerComponent.builder()
            .useCpu(new Cpu())
            .build();

需要注意這里傳入的參數不能為null,如果可能為 null爱榔,需要使用@Nullable 注解涂身。

Dagger2進階功能

  • @Subcomponent
  • Set, Map

前面的注入例子都是單個的對象,Dagger2還支持把多個單獨的對象注入到集合(Set搓蚪,Map)中蛤售,這塊很簡單,大家自己去看文檔把妒潭。

這里主要說一下另一個比較有用的功能悴能,@Subcomponent

從名字就看出來,Subcomponent 有著和 Component 類似的功能雳灾,事實上漠酿,他們的功能基本相同。Subcomponent 的主要用法是對某些子模塊的注入功能進行封裝谎亩,隱藏內部實現細節(jié)炒嘲。

Subcomponent 目前還沒怎么用過,具體例子待補充匈庭。

Dagger2在 Android 中的使用

在網上搜到的大部分關于 Dagger2的教程夫凸,都沒有寫到 Dagger2針對 Android 的特殊功能,所以特別的說明一下阱持。前面舉了很多例子夭拌,通過 Component 可以將所需要的對象注入到某個對象中,這就導致了一個問題衷咽,針對每個我們被注入的對象鸽扁,都需要寫一個 Component 接口來實現注入,在 Android 中镶骗,可能最常見的被注入的對象就是 Activity 和 Fragment 了桶现,通常的寫法是每個 Activity 和 Fragment都寫一個 Component。這里直接用一下官方文檔的例子:

public class FrombulationActivity extends Activity {
  @Inject Frombulator frombulator;

  @Override
  public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // DO THIS FIRST. Otherwise frombulator might be null!
    ((SomeApplicationBaseType) getContext().getApplicationContext())
        .getApplicationComponent()
        .newActivityComponentBuilder()
        .activity(this)
        .build()
        .inject(this);
    // ... now you can write the exciting code
  }
}

看到上面的一大串代碼了么鼎姊,是不是覺得好像沒什么問題骡和?

現在想象一下,你有10個 Activity此蜈,10個 Fragment即横,每個 Activity 和 Fragment 里面都要寫這么一長串,是不是很麻煩裆赵,最后的結果就是东囚,每個人都照著前面一個人寫的復制粘貼,久而久之战授,大部分人都不記得這段代碼是做什么的页藻,只是記得新做一個界面就要復制一份過去桨嫁。顯而易見,這樣復制粘貼的代碼并不是一個易于維護和修改的代碼份帐。

其次璃吧,在進行如上注入的時候,你需要首先知道這個 Activity 需要哪些依賴废境,并進行不同的設置畜挨,尤其是在使用接口的時候,需要知道要用的實現類噩凹,才能進行正確的注入巴元,這就打破了依賴注入一個很重要的原則,使用注入對象的類不應該知道如何創(chuàng)建所需要的對象驮宴。

為了解決上述問題逮刨,Dagger2特別提供了一個針對 Android 使用的簡化流程。

  1. 給你的Activity 寫一個 Subcomponent堵泽,應引用所需的 Module
@Subcomponent(modules = ComputerModule.class)
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
    @Subcomponent.Builder
    public abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
  1. 寫一個 Module 來使用這個 Subcomponent
@Module(subcomponents = MainActivitySubcomponent.class)
abstract class MainActivityModule {
    @Binds
    @IntoMap
    @ActivityKey(MainActivity.class)
    abstract AndroidInjector.Factory<? extends Activity>
    bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
}
  1. 現在需要一個 Top-Level Component 來進行注入修己,注意,一定要寫AndroidSupportInjectionModule迎罗,以及上面寫的 ActivityModule
@Component(modules = {
        AndroidSupportInjectionModule.class,
        MainActivityModule.class
})
@Singleton
interface AppComponent {
    void inject(MyApp app);
  1. 最后睬愤,修改你的 Application,實現HasActivityInjector接口佳谦,并在其中調用 AppComponent 進行注入

public class MyApp extends Application implements HasActivityInjector {
    @Inject
    DispatchingAndroidInjector<Activity> mActivityInjector;

    @Override
    public AndroidInjector<Activity> activityInjector() {
        return mActivityInjector;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        DaggerAppComponent.create().inject(this);
    }
}
  1. 最后一步戴涝,在你的 Activity 生命周期中調用注入方法
public class MainActivity extends AppCompatActivity {
    @Inject
    Computer mComputer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        AndroidInjection.inject(this);
        super.onCreate(savedInstanceState);
    }
}

之后添加新的 Activity 只需要重復1,2步钻蔑,并在AppComponent中加入新建的 Module 即可。

是不是覺得很復雜奸鸯,沒關系咪笑,還可以寫的更簡單:

@Module
abstract class MainActivityModule {
    @MyScope
    @ContributesAndroidInjector(modules = {ComputerModule.class})
    abstract MainActivity contributeYourActivityInjector();
}

用上面的代碼替換1,2步的內容即可娄涩。這種寫法適用于 Subcomponent 中不需要任何其他內容的情況窗怒。

寫了這么多,是不是覺得很奇怪蓄拣,為什么這么寫就能注入了扬虚?查看AndroidSupportInjectionModule的代碼可以看到,里面定義了Android 中常用的組件的 Map 類型的注入球恤,key 是對應的Class 類辜昵,而 value 這是上面第一步定義的MainActivitySubcomponent.Builder類,這個 Builder 類我們在第二步已經注入到 這個Map 中了咽斧,然后在 AndroidInjection 的 inject 方法中堪置,根據 Activity 的類型去獲取對應的 Builder 類進行注入躬存。

Android中其他組件的注入方式與 Activity 類似,就不多說了舀锨,大家可以自己去查看文檔岭洲。

這里還存在一些疑問,在 Subcomponent 的 Builder 類中坎匿,是可以添加@BindsInstance 注解傳入參數的盾剩,但是如果使用 AndroidInjectiion的 inject 方法進行注入,是無法傳入參數的替蔬,官方也沒有實際的例子可供參考告私,可能是我對 Subcomponent的理解還不夠,初步猜測可能需要自己重寫AndroidInejction进栽,在里面針對某個特定的需要參數的 Builder 進行單獨的初始化操作德挣。如果你有想法,歡迎一起討論快毛。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末格嗅,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子唠帝,更是在濱河造成了極大的恐慌屯掖,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,406評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件襟衰,死亡現場離奇詭異贴铜,居然都是意外死亡,警方通過查閱死者的電腦和手機瀑晒,發(fā)現死者居然都...
    沈念sama閱讀 92,732評論 3 393
  • 文/潘曉璐 我一進店門绍坝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人苔悦,你說我怎么就攤上這事轩褐。” “怎么了玖详?”我有些...
    開封第一講書人閱讀 163,711評論 0 353
  • 文/不壞的土叔 我叫張陵把介,是天一觀的道長。 經常有香客問我蟋座,道長拗踢,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,380評論 1 293
  • 正文 為了忘掉前任向臀,我火速辦了婚禮巢墅,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己砂缩,他們只是感情好作谚,可當我...
    茶點故事閱讀 67,432評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著庵芭,像睡著了一般妹懒。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上双吆,一...
    開封第一講書人閱讀 51,301評論 1 301
  • 那天眨唬,我揣著相機與錄音,去河邊找鬼好乐。 笑死匾竿,一個胖子當著我的面吹牛,可吹牛的內容都是我干的蔚万。 我是一名探鬼主播岭妖,決...
    沈念sama閱讀 40,145評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼反璃!你這毒婦竟也來了昵慌?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 39,008評論 0 276
  • 序言:老撾萬榮一對情侶失蹤淮蜈,失蹤者是張志新(化名)和其女友劉穎斋攀,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體梧田,經...
    沈念sama閱讀 45,443評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡淳蔼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,649評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了裁眯。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片鹉梨。...
    茶點故事閱讀 39,795評論 1 347
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖穿稳,靈堂內的尸體忽然破棺而出俯画,到底是詐尸還是另有隱情,我是刑警寧澤司草,帶...
    沈念sama閱讀 35,501評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站泡仗,受9級特大地震影響埋虹,放射性物質發(fā)生泄漏。R本人自食惡果不足惜娩怎,卻給世界環(huán)境...
    茶點故事閱讀 41,119評論 3 328
  • 文/蒙蒙 一搔课、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧截亦,春花似錦爬泥、人聲如沸柬讨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,731評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽踩官。三九已至,卻和暖如春境输,著一層夾襖步出監(jiān)牢的瞬間蔗牡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,865評論 1 269
  • 我被黑心中介騙來泰國打工嗅剖, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留辩越,地道東北人。 一個月前我還...
    沈念sama閱讀 47,899評論 2 370
  • 正文 我出身青樓信粮,卻偏偏與公主長得像黔攒,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子强缘,可洞房花燭夜當晚...
    茶點故事閱讀 44,724評論 2 354