最近在項目中使用了 Dagger2 這個依賴注入的框架,在這里記錄一下。第一次寫技術(shù)文章,不足之處請多指教。不過真的是寫出來才發(fā)現(xiàn)還是有很多不懂的地方。
介紹
- 什么是 Dagger2
下面這段是官網(wǎng)的簡介:
Dagger is a fully static, compile-time dependency injection framework for both Java and Android.
翻譯過來就是Dagger2是Java 和 Android 的一個編譯時生成代碼的依賴注入框架,如果你沒有了解過依賴注入,可能會感到不理解,什么是依賴注入,為什么要使用依賴注入,使用依賴注入有什么優(yōu)點,下面來簡單的說一下。
前置知識
- 什么是依賴注入 Dependency Injection
我們在實現(xiàn)一個功能的時候,往往需要創(chuàng)建很多對象,舉個例子,如果要實現(xiàn)一個Computer,需要幾個類:MotherBoard, Cpu, Computer。
在沒有依賴注入的時候,首先按順序構(gòu)建每個對象,先 new 一個 Cpu,然后 new 一個 MotherBoard,再用這兩個對象 new 一個 Computer,當(dāng)邏輯簡單的時候,這不會有什么問題,但是實際開發(fā)中要構(gòu)建的對象要遠(yuǎn)遠(yuǎn)比這要復(fù)雜的多,如果某個人修改了其中一個類的 Constructor,就要修改每個使用 Constructor 的地方,如果你的創(chuàng)建所有對象的代碼有成百上千行,那就很麻煩了。
使用依賴注入后,通過依賴注入的框架創(chuàng)建這些對象,不需要自己去維護依賴關(guān)系,你只需要去維護你一個依賴的配置,這就要簡單的多了。依賴注入的另一個好處,是對單元測試很友好,因為每個對象的創(chuàng)建并不依賴于特定的邏輯,在寫單元測試的時候,某些類的功能并不需要,或者無法在測試場景使用,那么只需要替換掉就可以了。在開發(fā)過程中,使用依賴注入也對多人并行開發(fā)有一定的好處,如上面的例子,可以一個人開發(fā) MotherBoard,一個人開發(fā) Cpu,一個人開發(fā) Computer,每個人都不需要關(guān)心其他人的對象要如何創(chuàng)建,因為每個對象的創(chuàng)建都是由依賴注入框架去維護的。
說到依賴注入的同時,一定會提起控制反轉(zhuǎn)(IOC),控制反轉(zhuǎn)是一種設(shè)計思路,意思就是說,原本我要用什么,是我自己創(chuàng)建,自己用,現(xiàn)在是我要用什么,我就管別人要,別人給什么用什么。這就又不得不提到一個軟件設(shè)計中的重要思想,依賴倒置原則。
那么什么是依賴倒置原則?
舉個例子,我們設(shè)計一臺電腦,先設(shè)計 cpu,再設(shè)計主板,最后設(shè)計機箱,這就存在一個依賴關(guān)系,主板依賴于 cpu的尺寸,機箱依賴于主板的尺寸,看起來是沒什么問題,現(xiàn)在我們要改一下設(shè)計,把 cpu 的尺寸做小一圈,這就蛋疼了,因為主板依賴于 cpu 的尺寸,主板需要重新設(shè)計,機箱依賴主板的尺寸,機箱也要重新設(shè)計,整個的設(shè)計都因此而發(fā)生了變化。
現(xiàn)在我們換一種思路,首先設(shè)計電腦整體的模型,據(jù)此設(shè)計機箱的結(jié)構(gòu),再根據(jù)機箱的結(jié)構(gòu)設(shè)計主板,根據(jù)主板的結(jié)構(gòu)去設(shè)計 cpu,現(xiàn)在要對 cpu 的進行修改,就只需要修改 cpu 自己就行了,并不會出現(xiàn)上面那種全部推倒重來的現(xiàn)象。
這就是依賴倒置原則,將原本由下至上的依賴關(guān)系翻轉(zhuǎn)過來,變?yōu)橛猩现料拢蠈記Q定下層功能,下層的修改對上層無任何影響,避免出現(xiàn)牽一發(fā)而動全身的現(xiàn)象。此時,上層與下層的依賴是依賴抽象,而非依賴實現(xiàn),這種方式能對上下層之間進行解耦,也能達到最大程度的復(fù)用。控制反轉(zhuǎn)即是依賴倒置原則的一種設(shè)計思路,而依賴注入則是這種思路的實現(xiàn)方式。
關(guān)于如何設(shè)計抽象關(guān)系,通常應(yīng)當(dāng)遵循面向軟件設(shè)計的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.”
這里簡單的總結(jié)一下,個人理解,軟件設(shè)計中的各種設(shè)計模式,原則,歸根結(jié)底,目的都是為了讓你的代碼更容易閱讀,擴展和維護,人不是機器,無法從數(shù)不清的代碼中快速找到自己需要的內(nèi)容,也無法在快速滾動的代碼中定位問題,而且一切都依賴于抽象,各種設(shè)計模式,原則,本質(zhì)上都是各種形式抽象,unix 中有一句話經(jīng)常提到,keep it simple,stupid,也是同樣的道理。依賴注入便是其中的一種實現(xiàn)方式。
- 常見注入方式
Java有很多依賴注入的框架,常見的有 Spring, Guice, Dagger1, Dagger2等。早期的注入方式是通過 xml 進行配置(Spring),xml注入的方式有很多缺點,比如說無法進行代碼檢查。在 Java5 出了之后,注解提供了另一個更便捷的方式,Spring 和 Guice 等框架支持了注解的方式,在JSR 330標(biāo)準(zhǔn)出現(xiàn)之后,更是有了統(tǒng)一的官方標(biāo)準(zhǔn),使用注解進行注入的好處是配置不容易出錯,因為代碼能編譯過,就說明寫的沒問題,但是此類注入都是通過修改編譯后生成的字節(jié)碼,或通過 JDK 提供的動態(tài)代理(Proxy),反射等方式實現(xiàn),修改后的字節(jié)碼無法進行 debug,運行時出錯很難排查。
Dagger2提供了另一種選擇,與其他依賴注入框架不同的是,Dagger2的注入方式是在編譯時生成代碼,通過 JDK 提供的 Annotation Processor Tool功能,在移動終端這種性能有限的環(huán)境下,生成代碼的方式更合適,性能也更好。但是也存在一些局限性,例如不支持任何動態(tài)注入,因為代碼在編譯后就已經(jīng)固定了,會增加編譯時間等。
以上內(nèi)容參考了這個視頻,有興趣的話可以看一下)
- Annotation Processor
上面提到了注解處理器,這里簡單的說一下,注解處理器是 Java5之后提供的一種在編譯時動態(tài)生成代碼的功能,通過讀取源碼中使用注解標(biāo)記的位置,使用自己的邏輯動態(tài)生成代碼。需要注意的是,生成代碼的時機是在編譯之前,在 Android的基于 Gradle 的編譯環(huán)境下,每個 Module 都需要指定自己的注解處理器。
Dagger2基本使用
現(xiàn)在開始說具體的使用,Dagger2使用JSR 330標(biāo)準(zhǔn)的API,和自己定義的一些注解,通過幾個簡單的注解和接口,就能進行注入操作。
首先要說明的是整體的一個思路,要記住的就是 No Magic,依賴注入從根本上來說,就是創(chuàng)建一個對象并給某個變量賦值,Dagger2的本質(zhì)就注解處理器在編譯時生成代碼,實際上就是對你所需要的變量進行賦值,而想要正確的給這個變量進行賦值,需要兩點:
- 要創(chuàng)建哪個對象
- 如何創(chuàng)建這個對象
第一點,首先要找到要創(chuàng)建哪個對象,Dagger2在匹配的時候通過類型和 Qualifier 注解來定位一個對象,所以不要在寫了兩個相同類型的注入并且編譯不通過的時候感到奇怪。
第二點,所有的注入對象,框架都要知道如何去創(chuàng)建它,以及它所依賴的其他所有對象,編譯不通過的時候,第一件事就是看報錯信息, Dagger2的報錯信息在大部分情況下都很詳細(xì),明確的指出了到底是缺少配置,還是配置錯誤,或者其他原因,框架也不能把一個對象變出來,如果你都不知道這個對象是怎么創(chuàng)建的,框架又怎么能知道呢?
看到這里可能比較疑惑,不著急,先往下看。
這里有幾個關(guān)鍵性的注解:
- @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 了。
如果你仔細(xì)看懂了上面的內(nèi)容,就會發(fā)現(xiàn)這段代碼并不能正確編譯,因為框架不知道如何創(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,現(xiàn)在完事齊全,只差注入了,沒有這最后一步,前面的一切都是沒用的。那么到底要怎么注入呢?現(xiàn)在需要另一個注解@Component
在 Dagger2 中,有一個 graph 的概念,其實每個依賴注入框架都有類似的概念,要注入的對象,及其所有依賴的對象之間,必然存在一個完整的依賴關(guān)系圖,在注入之前,必須要能完整的構(gòu)建出這個依賴關(guān)系圖。Component 注解標(biāo)記的類代表了一個完整的依賴關(guān)系的終點。當(dāng)然這一切都是框架去做的,你所需要的就是正確的配置出這個依賴關(guān)系。
@Component(modules = {
ComputerModule.class
})
public interface ComputerComponent {
void inject(Computer computer);
}
編譯后,dagger2會生成一個ComputerComponent 接口的實現(xiàn)類,類名為 Dagger + Component 名字,調(diào)用其中的方法就可以對 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 的類和方法,為什么都叫這樣的名字呢?在上面注入相關(guān)的方法,類名,及后面會說到的相關(guān)注入功能,方法名類名均與注入過程無關(guān),起什么名字只是為了方便閱讀,方便閱讀也是很重要的。
最簡單的說完了,下面來說一下各種不同場景的使用。
你可能已經(jīng)注意到了上面的DaggerComputerComponent是帶 Builder 的,那這個 Builder 是做什么的呢?
Provides 方法可以是static
的,也可以不是,如果不是,那么就需要一個 Module 對象來調(diào)用這個方法,這時候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 方法入?yún)⑴c返回值相同,僅僅是做了類型轉(zhuǎn)換,那么可以省略這個方法調(diào)用,以一個 abstract 方法代替,在注入時會直接使用傳入的參數(shù),如果你去看生成的代碼,就會發(fā)現(xiàn)實際并沒有調(diào)用你寫的這個 abstract 方法,而是直接使用了入?yún)⒌淖兞浚瑴p少了一個方法調(diào)用也是可以節(jié)約一些性能的,在Android這種性能有限的平臺上,還是很有意義的。
Scope
只要是依賴注入,一定會有一個 Scope 的概念,通常代表了注入對象的作用域。在 Dagger2,也就代表了這個注入的對象的生命周期。
一個常見的 Scope 是 @Singleton
,在可注入的類的類名上,或者 @Provides 標(biāo)記的方法上使用這個注解,表示改對象是單例的。使用的時候需要注意,如果使用Scope,對應(yīng)的 Component 也要使用此注解進行標(biāo)記。僅僅一個@Singleton 注解實際上并不能實現(xiàn)單例,單例的實現(xiàn)是依賴于 Component 的,從同一個 Component 對象進行注入,才是單例,如果你創(chuàng)建了兩個 Component用來注入一個單例,仍然會產(chǎn)生兩個不同的對象,如果打開生成的代碼進行查看,會有更深刻的印象。
同樣也可以自己定義 Scope
@Documented
@Retention(RUNTIME)
@CanReleaseReferences
@Scope
public @interface MyScope {}
事實上,自己定義的 scope,與@Singleton 并沒有什么區(qū)別,因為Scope 的實現(xiàn)都是基于Component,在同一個 Component 對象中,標(biāo)記了 Scope 的注入對象,都只會注入同一個對象,相當(dāng)于局部的單例,主要的目的是實現(xiàn)清晰的結(jié)構(gòu)。
實際使用中可能還會有這樣的場景,某些情況下不需要這個Component 中再保存單例對象,這時候可以將其釋放掉,仔細(xì)看上面的自定義 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的作用是把保存的對象轉(zhuǎn)移到一個 WeakReference 中,眾所周知WeakReference中的對象可以被回收,另一個方法restoreStrongReferences則可以把前面的 WeakReference 中的還沒有被回收的對象變回到強引用狀態(tài)。
關(guān)于ForReleasableReferences官方的例子是在內(nèi)存不足時釋放對象,但是實際應(yīng)用中并未發(fā)現(xiàn)實際的實用場景,待補充。
關(guān)于 Scope,還有一個@Reusable 注解,官方的解釋是針對某些特殊的可以隨便重復(fù)使用的對象,實際操作沒有發(fā)現(xiàn)什么區(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
標(biāo)記的注解,代替上面的@Named注解
@Qualifier
@Documented
@Retention(SOURCE)
public @interface MyComputer {
}
可選綁定
如果某個依賴對象允許不存在,可以在 Module 里使用@BindsOptionalOf
注解,這樣其他的需要依賴此對象的地方,都可以寫一個 Optional<MotherBoard> ,支持 guava 和 java8的 Optiona 類,不了解 Optiona 的可以自己查一下,主要是用來解決空指針異常問題的。也同樣支持 Lazy 和 Provider。
例如我現(xiàn)在設(shè)計了一種神奇的電腦可以沒有主板:
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();
現(xiàn)在即使不提供 MotherBoard 對象的注入,也不會報錯。
在一個 Module 中使用@BindsOptionalOf后,同樣的 Module 不允許使用@Provides 提供相同的注入對象,并且不能有使用@Inject 的 Constructor。當(dāng)有多個 Module 提供不同的依賴關(guān)系時,@BindsOptionalOf才有意義。
現(xiàn)在我修改了設(shè)計,又想要主板了,于是單獨寫一個MotherBoardModule用來提供主板:
@Module
class MotherBoardModule {
@Provides
static MotherBoard provideMotherBoard(Cpu cpu) {
return new MotherBoard(cpu);
}
}
又可以把主板注入到 Computer 中了。
BindsInstance
有時,Component 中需要注入的對象可能運行時才創(chuàng)建,這是可以使用@BindsInstance注解,給 Component 傳入一個可注入的對象。比如說現(xiàn)在我的電腦里的 Cpu 改為使用其他廠家的,不自己生產(chǎn)了,那么我只需要在構(gòu)建 Component 的時候作為一個參數(shù)傳進去就行了。
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();
需要注意這里傳入的參數(shù)不能為null,如果可能為 null,需要使用@Nullable 注解。
Dagger2進階功能
- @Subcomponent
- Set, Map
前面的注入例子都是單個的對象,Dagger2還支持把多個單獨的對象注入到集合(Set,Map)中,這塊很簡單,大家自己去看文檔把。
這里主要說一下另一個比較有用的功能,@Subcomponent
從名字就看出來,Subcomponent 有著和 Component 類似的功能,事實上,他們的功能基本相同。Subcomponent 的主要用法是對某些子模塊的注入功能進行封裝,隱藏內(nèi)部實現(xiàn)細(xì)節(jié)。
Subcomponent 目前還沒怎么用過,具體例子待補充。
Dagger2在 Android 中的使用
在網(wǎng)上搜到的大部分關(guān)于 Dagger2的教程,都沒有寫到 Dagger2針對 Android 的特殊功能,所以特別的說明一下。前面舉了很多例子,通過 Component 可以將所需要的對象注入到某個對象中,這就導(dǎo)致了一個問題,針對每個我們被注入的對象,都需要寫一個 Component 接口來實現(xiàn)注入,在 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
}
}
看到上面的一大串代碼了么,是不是覺得好像沒什么問題?
現(xiàn)在想象一下,你有10個 Activity,10個 Fragment,每個 Activity 和 Fragment 里面都要寫這么一長串,是不是很麻煩,最后的結(jié)果就是,每個人都照著前面一個人寫的復(fù)制粘貼,久而久之,大部分人都不記得這段代碼是做什么的,只是記得新做一個界面就要復(fù)制一份過去。顯而易見,這樣復(fù)制粘貼的代碼并不是一個易于維護和修改的代碼。
其次,在進行如上注入的時候,你需要首先知道這個 Activity 需要哪些依賴,并進行不同的設(shè)置,尤其是在使用接口的時候,需要知道要用的實現(xiàn)類,才能進行正確的注入,這就打破了依賴注入一個很重要的原則,使用注入對象的類不應(yīng)該知道如何創(chuàng)建所需要的對象。
為了解決上述問題,Dagger2特別提供了一個針對 Android 使用的簡化流程。
- 給你的Activity 寫一個 Subcomponent,應(yīng)引用所需的 Module
@Subcomponent(modules = ComputerModule.class)
public interface MainActivitySubcomponent extends AndroidInjector<MainActivity> {
@Subcomponent.Builder
public abstract class Builder extends AndroidInjector.Builder<MainActivity> {}
}
- 寫一個 Module 來使用這個 Subcomponent
@Module(subcomponents = MainActivitySubcomponent.class)
abstract class MainActivityModule {
@Binds
@IntoMap
@ActivityKey(MainActivity.class)
abstract AndroidInjector.Factory<? extends Activity>
bindMainActivityInjectorFactory(MainActivitySubcomponent.Builder builder);
}
- 現(xiàn)在需要一個 Top-Level Component 來進行注入,注意,一定要寫
AndroidSupportInjectionModule
,以及上面寫的 ActivityModule
@Component(modules = {
AndroidSupportInjectionModule.class,
MainActivityModule.class
})
@Singleton
interface AppComponent {
void inject(MyApp app);
- 最后,修改你的 Application,實現(xiàn)HasActivityInjector接口,并在其中調(diào)用 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);
}
}
- 最后一步,在你的 Activity 生命周期中調(diào)用注入方法
public class MainActivity extends AppCompatActivity {
@Inject
Computer mComputer;
@Override
protected void onCreate(Bundle savedInstanceState) {
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
}
之后添加新的 Activity 只需要重復(fù)1,2步,并在AppComponent中加入新建的 Module 即可。
是不是覺得很復(fù)雜,沒關(guān)系,還可以寫的更簡單:
@Module
abstract class MainActivityModule {
@MyScope
@ContributesAndroidInjector(modules = {ComputerModule.class})
abstract MainActivity contributeYourActivityInjector();
}
用上面的代碼替換1,2步的內(nèi)容即可。這種寫法適用于 Subcomponent 中不需要任何其他內(nèi)容的情況。
寫了這么多,是不是覺得很奇怪,為什么這么寫就能注入了?查看AndroidSupportInjectionModule的代碼可以看到,里面定義了Android 中常用的組件的 Map 類型的注入,key 是對應(yīng)的Class 類,而 value 這是上面第一步定義的MainActivitySubcomponent.Builder類,這個 Builder 類我們在第二步已經(jīng)注入到 這個Map 中了,然后在 AndroidInjection 的 inject 方法中,根據(jù) Activity 的類型去獲取對應(yīng)的 Builder 類進行注入。
Android中其他組件的注入方式與 Activity 類似,就不多說了,大家可以自己去查看文檔。
這里還存在一些疑問,在 Subcomponent 的 Builder 類中,是可以添加@BindsInstance 注解傳入?yún)?shù)的,但是如果使用 AndroidInjectiion的 inject 方法進行注入,是無法傳入?yún)?shù)的,官方也沒有實際的例子可供參考,可能是我對 Subcomponent的理解還不夠,初步猜測可能需要自己重寫AndroidInejction,在里面針對某個特定的需要參數(shù)的 Builder 進行單獨的初始化操作。如果你有想法,歡迎一起討論。