1.Dagger2簡介
1.1 Dagger2的描述
Github地址:Dagger2
Dagger2官網上介紹是:A fast dependency injector for Android and Java.
Dagger2是提供給Android和Java使用的快速依賴注入框架,目的是為了解耦.
1.2 Dagger2的前世今生
Dagger2是由谷歌接手開發,最早版本的Dagger1 是由Square公司開發的.
相比于Dagger1,Dagger2有更好的性能,它使用的是預編譯期間生成代碼來完成依賴注入,而不像Dagger1那樣通過反射完成依賴注入;還有一點,因為Dagger2是使用生成代碼來實現依賴注入的,所以可以在相關代碼處下斷點進行調試.
1.3 Dagger2的用處
使用Dagger2最本質的目的是為了模塊間解耦,提供代碼的健壯性和可維護性.
舉個簡單的例子
public class A{
? ? B b;
? ? public A(){
? ? ? ? b = new B()
????}
}
如上代碼,在ClassA對ClassB有個依賴,如果某一天ClassB的創建方式發生改變,即構造方法發生改變,這時不僅要修改ClassB中的代碼,還需要在所有創建ClassB的類中進行修改,如上面的ClassA.
如果使用Dagger2的話,在ClassA中可以這樣寫:
public class A{
? ? @Inject
? ? B b;
}
此時不管B的創建方式如何修改,在ClassA中都不需要進行修改.
2.Dagger2的原理
如上圖所示:
最左邊,是提供依賴的部分,可以通過使用@Inject注解其構造方法或者在Module中使用@Provides注解提供.
中間部分是Component,它本質上是被@Component注解的接口,通過@Component注解與Module建立聯系,并提供inject()函數.它是一個橋梁,負責連接提供依賴的部分和需要依賴的部分,
最右邊是需要依賴的地方,使用@Inject注解變量,就可以創建它的對象.
3.Dagger2的使用
3.1 Android studio中配置
在工程的build.gradle文件中添加android-apt插件
dependencies {
? ? ?... // 其他classpath
? ? ?classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' //添加apt命令
?}
module的build.gradle添加
apply plugin: 'com.neenbedankt.android-apt'//添加apt命令
dependencies {
? ? apt 'com.google.dagger:dagger-compiler:2.16' //指定注解處理器
? ? compile 'com.google.dagger:dagger:2.16' ?//dagger公用api
? ? provided 'org.glassfish:javax.annotation:10.0-b28' ?//添加android缺失的部分javax注解庫
}
但是當Android studio升級到3.0之后,就會出現以下錯誤:
Error:android-apt plugin is incompatible with the Android Gradle plugin. ?Please use 'annotationProcessor' configuration instead.
解決辦法:Studio升級到3.0之后原來的配置方式apt與最新版本Gradle已經不兼容,推薦使用annotationProcessor
只需要添加這三個依賴就可以,工程的build.gradle與app的build.gradle插件可不用添加
首先在Android studioModule的Gradle中添加如下代碼:
// Add Dagger dependencies
dependencies {
? compile 'com.google.dagger:dagger:2.16'
? annotationProcessor 'com.google.dagger:dagger-compiler:2.16'
?compile 'org.glassfish:javax.annotation:10.0-b28'?//添加android缺失的部分javax注解庫
}
3.2 常用注解
@Inject:
通常在需要依賴的地方使用這個注解。換句話說,你用它告訴Dagger這個類或者字段需要依賴注入。這樣,Dagger就會構造一個這個類的實例并滿足他們的依賴。
@Module:
Modules類里面的方法專門提供依賴,所以我們定義一個類,用@Module注解,這樣Dagger在構造類的實例的時候,就知道從哪里去找到需要的 依賴。modules的一個重要特征是它們設計為分區并組合在一起(比如說,我們的app中可以有多個組成在一起的modules)
@Provide:
在modules中,我們定義的方法是用這個注解,以此來告訴Dagger我們想要構造對象并提供這些依賴。
@Component:
Components從根本上來說就是一個注入器,也可以說是@Inject和@Module的橋梁,它的主要作用就是連接這兩個部分。
Components可以提供所有定義了的類型的實例,比如:我們必須用@Component注解一個接口然后列出所有所提供的依賴.
@Qualifier:
要作用是用來區分不同對象實例
@Named 其實是@Qualifier的一種實現
3.3 簡單示例
被需要依賴的類
public class AManager {
? ? public void makeShow() {
? ? ? ? Toast.makeText(AppUtils.getApp(), "Hello Dagger2!!!", Toast.LENGTH_SHORT).show();
? ? }
}
提供依賴的類:Module
@Module
public class ManagerModule { ? ?
? ? @Provides
? ? public AManager provideManagerA(String name) {
? ? ? ? Log.e("TAG", "provideManagerA");
? ? ? ? return new AManager();
? ? }
}
中間橋梁:Contenent
@Component(modules = {ManagerModule.class})
public interface ManagerComponent {
? ? void inject(MainActivity activity);
}
需要依賴的類:Container
public class MainActivity extends AppCompatActivity {
? ? @Inject
? ? AManager aManager;
? ? @Override
? ? protected void onCreate(Bundle savedInstanceState) {
? ? ? ? super.onCreate(savedInstanceState);
? ? ? ? setContentView(R.layout.activity_main);
? ? ? ? DaggerManagerComponent.create().inject(this);
? ? ? ? findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(View view) {
? ? ? ? ? ? ? ? aManager.makeShow();
? ? ? ? ? ? }
? ? ? ? });
? ? }
}
3.4 關于模塊化的實現
在項目中,出于更加靈活以及可維護等其他原因,常進行模塊化的劃分,比如B功能的實現需要A,C功能的實現也需要A,那么我們就需要單獨對A進行處理,以便于其他功能模塊的使用.
Dagger有三種途徑進行模塊之間的依賴:
方式一:Module中通過@Module注解,includes其他的Module
@Module(includes = {xxxModule.class})
方式二:Component中通過@Component注解關聯需要的其他Module
@Component(modules = {ManagerModule.class,xxxxModule.class})
方式三:Component中通過@Component注解dependencies 其他Component
@Component(modules = {ManagerModule.class},dependencies = {xxxComponent.class})
注意事項:
通過方式三與其他模塊關聯時,被需要依賴的類的構造方法需要使用@Inject進行注解,同時在注入Container的時候把其他的Component進行注入.
3.5 創建和區分不同實例
>>>>>>>方式一:使用@Named注解
@Module
public class ManagerBModule {
? ? @Named("release")
? ? @Provides
? ? public BManager provideManagerB_Release(AManager aManager) {
? ? ? ? BManager bManager = new BManager(aManager);
? ? ? ? Log.e("TAG", "provideManagerB_Release:" + bManager);
? ? ? ? return bManager;
? ? }
? ? @Named("debug")
? ? @Provides
? ? public BManager provideManagerB_Debug(AManager aManager) {
? ? ? ? BManager bManager = new BManager(aManager);
? ? ? ? Log.e("TAG", "provideManagerB_Debug:" + bManager);
? ? ? ? return bManager;
? ? }
}
? ? @Named("release")
? ? @Inject
? ? BManager bManager_release;
? ? @Named("debug")
? ? @Inject
? ? BManager bManager_debug;
>>>>>>>方式二:使用自定義注解
模仿Named注解,進行自定義
自定義Release注解:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Release {
}
自定義Debug注解:
@Qualifier
@Documented
@Retention(RUNTIME)
public @interface Debug {
}
在Module中使用:
@Module
public class ManagerBModule {
? ? @Release
? ? @Provides
? ? public BManager provideManagerB_Release(AManager aManager) {
? ? ? ? BManager bManager = new BManager(aManager);
? ? ? ? Log.e("TAG", "provideManagerB_Release:" + bManager);
? ? ? ? return bManager;
? ? }
? ? @Debug
? ? @Provides
? ? public BManager provideManagerB_Debug(AManager aManager) {
? ? ? ? BManager bManager = new BManager(aManager);
? ? ? ? Log.e("TAG", "provideManagerB_Debug:" + bManager);
? ? ? ? return bManager;
? ? }
}
在Activity中的使用:
? ? @Release
? ? @Inject
? ? BManager bManager_release;
? ? @Debug
? ? @Inject
? ? BManager bManager_debug;
3.6 Singleton單例講解
使用@Singleton注解,需要注意的是如果Module中,@Provides注解的方法使用了@Singleton注解,那么與之對應的Component也要加上@Singleton注解.
Module代碼:
Component代碼:
Activity代碼:
點擊按鈕后的Log日志:
注意事項:
Dagger2中的單例與Java中的單例不同,Java中的單例對象是存放在靜態區的,而Dagger2的單例是與Component相關聯的.不同Component所提供的實例化的單例對象是不同的.
3.7關于自定義Scope
由于component的dependencies與component自身的scope不能相同,即組件之間的scope不同,因此需要自定義一些Scope
對于應用級的AppComponent常使用@Singleton,以使某些對象在整個程序中只實例化一次
Module:
Component:
Application:
自定義的ActivityScope
依賴AppComponent的Component:
Activity代碼:
關于Scope推薦一篇文章:Dagger2Scope講解
有關Scope注解參照@Singleton注解
@Scope :注明是Scope ,表示作用域范圍
@Documented :標記在文檔
@Retention(RUNTIME) :運行時級別
Scope的生命周期是依附于其容器的,當其所依附的Application或者是Activity銷毀后,其關聯的Module中的實例化對象也會被銷毀.
3.8關于Subcomponent
在Dagger2中,Component之間存在兩種關系,依賴關系和繼承關系
依賴關系:一個 Component 依賴其他 Component 公開的依賴實例,使用 @Component 注解中的dependencies進行聲明.
繼承關系:一個 Component 繼承某個Component 提供更多的依賴,SubComponent 就是繼承關系的體現.
兩者之間的比較:
>相同點:
? ??兩者都能復用其他 Component 的依賴
????有依賴關系和繼承關系的 Component 不能有相同的 Scope
>不同點:
? ? ? 依賴關系中被依賴的 Component 必須顯式地提供公開依賴實例的接口,而 SubComponent 默認繼承 parent Component 的依賴.
? ? ? 依賴關系會生成兩個獨立的 DaggerXXComponent 類,而 SubComponent 不會生成 獨立的 DaggerXXComponent 類.
示例:
父Component的Module:
父Component:
子Component的Module相關:
子Component:
Activity代碼:
注意事項:
1>Subcomponent同時具備兩種不同生命周期的scope, SubComponent具備了父Component擁有的Scope,也具備了自己的Scope.
2>SubComponent的Scope范圍小于父Component
3.9拓展-關于Lazy與Provider:
Lazy(延時注入):
有時當需要注入的依賴在使用時再完成初始化,加快加載速度,就可以使用注入Lazy<T>.只有在調用 Lazy 的 get() 方法時才會初始化依賴實例注入依賴.
好比component初始化了一個present對象,然后放到一個池子里,需要的時候就get它,所以你每次get的時候拿到的對象都是同一個;并且當你第一次去get時,它才會去初始化這個實例.
provider(強制加載):
有時候不僅僅是注入單個實例,我們需要多個實例,這時可以使用注入Provider<>,每次調用它的 get() 方法都會調用到?@Inject 構造函數創建新實例或者 Module 的 provide 方法返回實例。
注意事項:
要想調用Porvder的get()到的實例對象不同,必須其Module中提供實例的方法沒有被@Scope注解,不然所獲取到的是同一對象.
4 注意事項
1. Component 的 inject(自定義,可以隨便起名) 方法如果接收的是父類型參數,而調用時傳入的是子類型對象則無法實現注入;
2. Component中使用@Component注解關聯的modules中的Module內不能有重復的provide,除非使用@Qualifier注解進行限定,如@Named注解;
3. Module的@Provides注解的方法使用了 Scope ,那么與其關聯的Component 就必須使用同一個Scope注解;如果Module中@Provides注解的方法沒有使用Scope,那么與其關聯的Component是否加Scope注解都無所謂,可以通過編譯;
4.Component中使用@Component注解聲明的dependencies與Component自身的Scope不能相同,即組件之間的Scope必須不同;
5.@Singleton注解的組件不能依賴其他Scope的組件,只能其他Scope的組件依賴Singleton的組件;
6.沒有Scope的Component不能依賴有Scope的Component;
7.一個Component不能同時有多個scope(Subcomponent除外);
8.@Singleton 的生命周期依附于Component,同一個Module provide singleton實例對象 ,不同Component 也是不一樣的;
9.已使用Component注入的Container不能被其他Component二次注入.
?