Dagger 是 Java 平臺的依賴注入庫。在 J2EE 開發上流行甚廣的 Spring 就是一個依賴注入庫。此外還有 Google 的 Guice 和 Square 的 Dagger1。但它們都是是經過在運行時讀取註解來實現依賴注入的,依賴的生成和注入須要依靠 Java 的反射機制,這對於對性能很是敏感的 Android 來講是一個硬傷。java
Dagger 一樣使用註解來實現依賴注入,但它利用 APT(Annotation Process Tool) 在編譯時生成輔助類,這些類繼承特定父類或實現特定接口,程序在運行時 Dagger 加載這些輔助類,調用相應接口完成依賴生成和注入。Dagger 對於程序的性能影響很是小,所以更加適用於 Android 應用的開發。程序員
在瞭解 Dagger2 以前,請務必先通曉兩個概念:編程
依賴注入(Dependency Injection : DI):面向對象編程的一種模式,最大的做用是解耦。安全
Java 註解(Annotation):瞭解到哪些是 Java 原生支持的註解。app
下文嘗試儘可能拋開特定的場景(好比 MVP 模式),以更具備普適性的代碼,從骨架開始,慢慢去豐滿血肉,更全面的去理解這裏這些註解的使用。ide
假設 MainActivity 依賴一個 Tester 的類,代碼應該以下:函數
public class Tester { @Inject public Tester() { } }
public class MainActivity extends Activity { @Inject Tester tester; }
@Inject 有兩個做用:性能
標記 Tester 的構造方法,通知 Dagger2 在須要該類實例時能夠經過該構造函數 new 出相關實例從而提供依賴。提供依賴的方式還有 @Provide ,下面細說。ui
標記 MainActivity 的 Tester 變量,通知 Dagger2 該變量實例須要由它來提供,也就是上述的須要 Dagger2 去 new 出 Tester 的實例 tester 。this
咱們另外還須要一箇中間件去鏈接依賴提供方和依賴使用方,這就是 @Component ,詳細的內容在下面介紹,先看下這個例子中的 TestComponent:
@Component public interface TestComponent { void inject(CActivity cActivity); }
在 MainActivity 的 onCreate() 加入:
@Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); ... DaggerTestComponent.builder().build().inject(this); ... }
DaggerTestComponent 是 Dagger2 幫咱們生成的類,命名規則就是在咱們定義的接口名稱前加上Dagger,實現了咱們以前定義的 TestComponent 接口。執行完這一行代碼,tester 就已經不是 null 了。
@Inject 標記構造方法來生成依賴對象的方式有它的侷限性,如:
接口(Interface),沒有構造方法,天然無處標記。
第三方庫提供的類,不適合去直接修改源碼,標記構造方法。
對於上述的問題,就須要 @Provide 來提供依賴:
@Module public class TestModule { @Provides Tester provideTester() { return new Tester(); } }
須要注意的是:
@Provides 只能用於標記非構造方法,而且該方法必須在 @Moudle 內部。
@Provides 修飾的方法的方法名建議以 provide 開頭。
@Component(modules = TestModule.class) public interface TestComponent { void inject(CActivity cActivity); }
這裏跟以前使用 @Inject 提供依賴時的 Component 不一樣,標識了提供依賴的 TestModule。
public class MainActivity extends Activity { @Inject Tester tester; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); DaggerTestComponent.builder().testModule(new TestModule()).build().inject(this); } }
DaggerTestComponent 也一樣多了 testModule() 方法,參數就是咱們本身定義的 TestModule。
@Module 通常用來標記類,該註解告知 Dagger2 能夠到該類中尋找須要的依賴。上述的 @Provides 則標記提供依賴實例的方法。二者都是一塊兒使用的。
Component通常用來標註接口,如上文所說,做用在於做爲依賴提供方和依賴使用方溝通的橋樑。
更重要的一點在於:Component 能夠組合不一樣的 Module 和 Component。
@Component(dependencies = ApplicationComponent.class, modules = ActivityModule.class) public interface ActivityComponent {}
多個 Module 和 Component 的狀況:
@Component( dependencies = {Component1.class,Component2.class,...}, modules = {Module1.class,Module1.class,...}) public interface ActivityComponent {}
敲黑板,劃重點:
Component 和它依賴的其餘 Component 的 @Scope 做用域(@Scope下文會詳細介紹)不能相同。
若是在依賴使用方須要依賴的對象並非由當前直接的 Component 的 Module 提供的,而是由其所依賴的其餘 Component 的 Module 提供的。那麼就在被依賴的 Component 中就須要提供一個返回值爲這個被依賴的 Compnent 的 Module 提供的依賴對象的方法,方法名能夠隨意。(賊™繞,血崩!)
舉個栗子:
這裏有一個 AModule 和 AComponent:
@Module public class AModule { @Provides Tester provideTester() { return new Tester(); } }
@Component(modules = AModule.class) public interface AComponent {}
還有一個依賴於 AComponent 的 BComponent:
@Component(dependencies = AComponent.class) public interface BComponent { void inject(MainActivity mainActivity); }
在 MainActivity 中就依賴 Tester 對象:
public class MainActivity extends Activity { @Inject Tester tester; }
這麼寫的話,編譯器就不幹了...咱們須要在被依賴的 ACompnent 中添加返回值爲 Tester 的方法,以下:
@Component(modules = AModule.class) public interface AComponent { Tester getTester(); }
栗子舉完了,可是這裏我仍有個疑惑,爲什麼這裏這麼不智能?須要顯示去提供依賴?明明 @Subcomponent (下文詳述)就能夠很智能的去父 Component 中查找缺失的依賴...
@Subcomponent 其功能效果相似 component 的 dependencies。可是使用 @Subcomponent 不須要在父 component 中顯式添加子 component 須要用到的對象,只須要添加返回子 Component 的方法便可,子 Component 能自動在父 Component 中查找缺失的依賴。
// 父Component @PerApp @Component(modules = AppModule.class) public AppComponent{ ActivityComponent getActivityComponent(); // 1.只須要在父Component添加返回子Component的方法便可 } // 子Component @PerAcitivity // 2.注意子 Component 的 Scope 範圍小於父 Component @Subcomponent(modules = ActivityModule.class) // 3.使用 @Subcomponent 註解 public ActivityComponent{ void inject(MainActivity activity); } public class App extends Application { AppComponent mAppComponent; @Inject ToastUtil toastUtil; @Override public void onCreate() { super.onCreate(); mAppComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build(); } public AppComponent getAppComponent(){ return mAppComponent; } } public class SomeActivity extends Activity{ public void onCreate(Bundle savedInstanceState){ ... App.getAppComponent().getActivityComponent().inject(this);//4.調用getActivityComponent方法建立出子Component } }
@Subcomponent 和 Component 在使用最大的差別就在於:
當咱們使用的 @Subcomponent,父 Component 能夠徹底不暴露本身,而只把子 Component 傳遞給其使用者。
這段內容引自:Dagger 源碼解析
若是有兩類程序員,他們的能力值 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;
此外,還有一個默認實現的註解 @Named,使用方法同上,源碼以下:
@Qualifier @Documented @Retention(RUNTIME) public @interface Named { /** The name. */ String value() default ""; }
Dagger2 能夠經過 @Scope 自定義註解限定註解做用域。
咱們直接看 Dagger2 自帶的,經過 @Scope 實現的 @Singleton 註解,源碼以下:
@Scope @Documented @Retention(RUNTIME) public @interface Singleton{}
用法以下:
@Singleton // @Inject 提供對象的單例模式 public class Tester { @Inject public Tester() {} }
@Provides @Singleton // @Provides 提供對象的單例模式 Tester provideTester() { return new Tester(); }
@Singleton // 標明該 Component 中有 Module 使用了 @Singleton @Component(modules = TestModule.class) public interface ActivityComponent { void inject(); }
若是要使用咱們本身的註解,好比在 MVP 中很是常見的 @PreActivity,將上面的 @Singleton 替換成 @PreActivity 便可,使用上無區別:
@Scope @Documented @Retention(RetentionPolicy.RUNTIME) public @interface PreActivity {}
這裏咱們使用一個最簡單的 @Singleton 註解提供一個 AppComponent。編譯後,Dagger2 自動幫咱們生成以下代碼:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class DaggerAppComponent implements AppComponent { private Provider<Context> provideContextProvider; private MembersInjector<App> appMembersInjector; private DaggerAppComponent(Builder builder) { assert builder != null; // 判斷了只有第一次實例化這個Component時纔會去執行下面的代碼 initialize(builder); } public static Builder builder() { return new Builder(); } private void initialize(final Builder builder) { this.provideContextProvider = ScopedProvider.create(AppModule_ProvideContextFactory.create(builder.appModule)); this.appMembersInjector = App_MembersInjector.create((MembersInjector) MembersInjectors.noOp(), provideToastUtilProvider); } @Override public Context context() { return provideContextProvider.get(); } @Override public void inject(App app) { appMembersInjector.injectMembers(app); } public static final class Builder { private AppModule appModule; private Builder() { } public AppComponent build() { if (appModule == null) { throw new IllegalStateException("appModule must be set"); } return new DaggerAppComponent(this); } public Builder appModule(AppModule appModule) { if (appModule == null) { throw new NullPointerException("appModule"); } this.appModule = appModule; return this; } } }
傳遞進來的 appModule 首先交由 AppModule_ProvideContextFactory:
@Generated("dagger.internal.codegen.ComponentProcessor") public final class AppModule_ProvideContextFactory implements Factory<Context> { private final AppModule module; public AppModule_ProvideContextFactory(AppModule module) { assert module != null; this.module = module; } @Override public Context get() { Context provided = module.provideContext(); // 這就是咱們在 AppModule 中定義的方法,去提供 Context 對象的實例。 if (provided == null) { throw new NullPointerException("Cannot return null from a non-@Nullable @Provides method"); } return provided; } public static Factory<Context> create(AppModule module) { return new AppModule_ProvideContextFactory(module); } }
這個類的功能其實就是維護了 Context 對象,其餘類在想使用時就能夠從這裏拿。
繼續看以前,AppModule_ProvideContextFactory 經過工廠模式建立了本身的實例後就把本身傳遞給了 ScopedProvider:
public final class ScopedProvider<T> implements Provider<T> { private static final Object UNINITIALIZED = new Object(); private final Factory<T> factory; private volatile Object instance = UNINITIALIZED; private ScopedProvider(Factory<T> factory) { assert factory != null; this.factory = factory; } @SuppressWarnings("unchecked") // cast only happens when result comes from the factory @Override public T get() { // double-check idiom from EJ2: Item 71 Object result = instance; if (result == UNINITIALIZED) { synchronized (this) { result = instance; if (result == UNINITIALIZED) { instance = result = factory.get(); } } } return (T) result; } /** Returns a new scoped provider for the given factory. */ public static <T> Provider<T> create(Factory<T> factory) { if (factory == null) { throw new NullPointerException(); } return new ScopedProvider<T>(factory); } }
對以前的 Context 對象,作一次雙重校驗鎖,目的是爲了實現對象的線程安全。
在用到 Context 對象的地方,都是相似於 DaggerAppComponent 中的 provideContextProvider.get() 方法去獲取實例。
總結來講,@Scope自己並不控制對象的生命週期,其生命週期其實仍是看生成的 Component 對象的生命週期。
其實知曉 Dagger2 註解的使用,大體瞭解 Dagger2 的原理其實並非難點或者說重點。 在實際工程中如何靈活去使用它,如何根據業務的須要切分 Module 和 Component 纔是咱們在以後須要時間不斷去打磨。