Android的依賴注入框架-Dagger2(一)

關於依賴注入

在談 Dagger2 之前,我想先簡單介紹一下關於 依賴注入 。什麼是依賴注入? Dependency Injection (依賴注入) 簡稱 DI,用來降低程式碼的耦合度所使用。

當我們今天有一個 Class A 用到了 Class B 的物件,一般情況下,需要在 A 的代碼中顯示的 new 一個 B 的物件。這樣子Class A 的建構就依賴於 Class B了,
當我們實現依賴注入之後 A 的代碼只需要定義一個私有的 B 物件,不需要直接
New 來獲得這個物件,而是透過相關的容器控制程式來將 B 物件在外部 new出來並注入到 A 類裡的參考中。

依賴注入有如下實現方式:

  • 基於介面。實現特定介面以供外部容器注入所依值型別的物件。
  • 基於 set 方法。實現特定屬性的public set方法,來讓外部容器呼叫傳入所依值型別的物件。
  • 基於建構函式。實現特定參數的建構函式,在新建物件時傳入所依值型別的物件。
  • 基於註解。基於Java的註解功能,在私有變數前加「@Autowired」等註解,不需要顯式的定義以上三種代碼,便可以讓外部容器傳入對應的物件。該方案相當於定義了public的set方法,但是因為沒有真正的set方法,從而不會為了實現依賴注入導致暴露了不該暴露的介面(因為set方法只想讓容器存取來注入而並不希望其他依賴此類的物件存取)。

Dagger2 簡介

Dagger is a fully static, compile-time dependency injection framework for both Java and Android. It is an adaptation of an earlier versioncreated by Square and now maintained by Google.
Dagger aims to address many of the development and performance issues that have plagued reflection-based solutions. More details can be found in this talk(slides) by +Gregory Kick.

簡單來說 Dagger2 是原本 Square 開發出來給 Java/Android 的一個 DI 框架,然後現在主要維護者是 Google

在實際使用 Dagger2 之前,這邊先介紹幾個 Dagger2 中一些主要 註解 觀念與用法。

  • @Inject
  • @Module
  • @Providers
  • @Component

@Inject

@Inject 主要有兩個用法,當註解在需要依賴的變數時,讓 Dagger2 為其提供依賴,另一個是使用在建構子上,透過建構子讓Dagger2來使用(Dagger2透過 Inject 註解可以在需要這個 class 實體的時候來找到這個建構子並把相關實體 new 出來) 從而提供依賴。需要注意的當 @Inject 註解的建構子是帶參數的,像是 :

1
2
3
4
5
6
public class TestClass{
@Inject
public TestClass(InjectClass injectClass){
//do something
}
}

那麼 Dagger2 會在編譯時檢查,是否在 Module 類中有返回值是 InjectClass 的方法,或者 InjectClass 是否有被 @Inject 註解的建構子。如果沒有找到,就會再編譯期報錯。確保在編譯期就滿足對象之間的依賴關係。

@Module

@Module 主要用來提供依賴類別。Module其實是一個簡單工廠模式,裡面包含了許多 @Provider 的方法,每個方法提供了該類別的實體 。

@Provider

@Provider@Module 是一對,@Module 是一個註解類別, @Provider 則是類別中提供實體的註解方法。Dagger2 會在需要建立實體時找到這個方法並呼叫,完成對象的實體化。像是下列例子, providerFactory() 會提供 FactoryClass 的實體。

1
2
3
4
5
6
7
@Module
public class ModuleClass {
@Provides
FactoryClass provideFactory() {
return new FactoryClass();
}
}

@Component

@Component 主要是一個橋樑,在前面說到了 @Module 提供了依賴的實體, @Inject 標記了需要依賴的變數,而 @Component 則是將 @Module 提供的實體注入到標記 @Inject 的變數中。

@Component 主要的職責 :

  • 誰提供依賴的實體。
  • 此 Component 依賴哪些其他的 Component。
  • 此 Component 為誰提供依賴注入。
  • 此 Component 可以提供哪些依賴實體。
1
2
3
4
5
@Component(modules = {AppModule.class, TestModule.class},dependencies = {DependciesComponent.class})
public interface AppComponent{
void inject(InjectActivity activity);
public ProviderClass getProviderClass();
}

@Component 標記一個 Interface ,從上面的例子對照上述的職責來看,此 Component 可以提供一個 ProviderClass 的實體。

  • module 就表示提供依賴的 Module 類別。
  • dependencies 表示該 Component 還依賴於其他的 Component 提供的依賴類別。
  • inject 方法表示該 Component 注入的目標為 InjectActivity
  • getProviderClass 方法透過此 Componet 介面,明確告訴外部這個Component 提供哪些依賴類別。

最後被 @Component 註解的介面實現會由 Dagger2 自動產生,可以在 app-build 的目錄底下找到產生的代碼。

參考文獻

https://ddnews.me/world/prycvggt.html