什么是Dagger2
1.Dagger2是一個依賴注入框架。如果Class A 內部 有一個Class B,我們稱作Class A依賴Class B
例如:
public class A{
B? b
public A(){
b=new B();
}
}
上述方法代碼耦合度很高,如果B是一個父類,我們需要在A中用到不同的B類,是需要不斷創建對象B b1= new B1(),B b2=new B2();
2.依賴注入:是外界根據需要注入不同的對象。這樣可以有效的降低耦合度。
像這種非自己主動初始化依賴,而通過外部來傳入依賴的方式,我們就稱為依賴注入。
參考資料:https://github.com/android-cn/blog/tree/master/java/dependency-injection
public class A{
B b;
public A(B b){
this.b=b;
}
}
安卓應用在初始化對象的時候經常需要處理各種依賴關系。比如說網絡訪問中使用Retrofit,Gson,本地存儲中使用shared preference。無一例外,我們都都需要在使用它們的地方進行實例對象構建,對象之間可能還存在著各種各樣的依賴關系。依賴注入(Dependency Injection,簡稱DI)是用于削減計算機程序的耦合問題的一個法則。對象在被創建的時候,由一個調控系統內所有對象的外界實體將其所依賴的對象的引用傳遞給它。也可以說,依賴被注入到對象中。Dagger2 正是一個依賴注入框架,使用代碼自動生成創建依賴關系需要的代碼。減少很多模板化的代碼,更易于測試,降低耦合,創建可復用可互換的模塊。
*文/CameloeAnthony(簡書作者)
原文鏈接:http://www.lxweimin.com/p/01d3c014b0b1
一、配置Dagger2。
1.在項目的gradle中加入apt的依賴
Paste_Image.png
2.在module的gradle中加入dagger2需要依賴的庫
Paste_Image.png
3.在module的gradle中加入插件支持
Paste_Image.png
4.如果已經都配置好了,那么需要makeproject一下。
在AndroidStudio中,點擊一下紅線標記的按扭
Paste_Image.png
二、Dagger2的使用
圖片來自參考資料.png
1、既然是依賴注入,那么肯定有提供依賴的地方,在Dagger2中提供依賴的類被標注為Module,在Module中,具體提供對象的方法,被Provides注解標記。
2、從流程圖中可以看出,Module中提供A對象給B和C。傳遞給B和C的工作由Component注解標記的接口來進行。
3、在B和C哪個對象要通過注入拿到對象則通過Inject注解進行標示。
圖片來自參考資料
1.Inject注解
在Activity中,為LogUtils加上Inject注解,告訴編譯器LogUtils需要通過注入的方式實例化。同樣可以使用@inject注解標注構造方法,方法等。
注意,被inject標注的不能是private修飾的對象。
interface是不能被構建的
第三方的類不能被注解
通過配置得到的對象,仍然必須通過配置得到(Builder模式構造的對象)
如果使用@Inject標注了參數,如果沒有通過Module提供依賴,會去查找是否有@Inject標注的構造方法,
使用該構造方法提供依賴,如果該構造方法中也有參數,會去Module中查找是否提供了依賴。
Paste_Image.png
2.Module
Module是一個提供依賴對象的地方,需要使用@Module標注類,使用@Provide標注提供依賴的方法
編譯器需要知道LogUtils的對象在哪里能夠得到。這時需要一個提供對象的類,加上Module注解
如果使用了@Scope注解為方法標注了使用的作用域,那么Component類上也需要有相應的作用域,否則編譯報錯。
注意AppModuls的方法名是任意的,為了能更好的讀懂代碼一般都稱作provider+對象名稱。
Paste_Image.png
3.Component
1.有了提供的地方,需要一個橋梁讓被標記為Inject的對象能夠拿到自己對應的對象,因為Moduls類只是提供不負責傳遞。所以需要一個Component接口,被標記為Component的類就是這個媒介。
我們需要提供一個inject方法,并且參數一定要是對應的Activity,Fragment類,例如我在MainActivity中需要依賴注入,參數一定要為MainActivity,當然Component可以有多個inject方法,為不同的頁面提供注入。
全局的AppComponent只能含有一個帶參的無返回值方法,用來給對應的地方實行注入。如:
如果當前AppComponent被其他Component依賴了。其他Component提供依賴時,需要顯示的提供依賴,即提返回對應依賴的無參方法。如:AppComponent
@Component(modules = {AppModule.class})
public interface AppComponent {
void inject(ContainerActivity activity);
Context getContext();
}
注意除了使用dependencies對當前的component進行擴展,還可以使用subcomponent進行擴展
@dependencies 代表需要哪一個component提供依賴
@module 代表通過哪一個module提供依賴。
如果你的module指定了作用域,那么component也要使用相同的注解標定作用域
component支持多個module? 可以使用,號拼接,也可以使用下列寫法。
@Module(includes={ModuleA.class,ModuleB.class,ModuleC.class})
public class FruitModule{
...
}
@Component(modules={FruitModule.class}) //添加多個Module
public interface FruitComponent{
...
}
Paste_Image.png
4.在Application中將Component橋梁實例化
如果你的配置都正確,并且makeproject了,編譯器會自動為你的Component生成代碼,名稱是Dagger+你的Component名稱。如果沒有這個類,請查看在Gradle中配置是否正確,并且是否makeproject了。
Paste_Image.png
5.Activity拿到中介(橋梁Component),為對象注入
提供的中介當然就是我們在Application中實例化的Component了。
Paste_Image.png
三、Component的之間的依賴---SubComponent
除了使用@Component的dependience注解擴展當前component的支持的依賴,還可以通過SubComponent進行擴展,這有點像繼承關系。并且父Component不用顯示的拋出支持的依賴,只需添加一個返回子Component的方法
下面看一個來自于鴻洋Blog的圖片
http://chuansong.me/n/400687651115
使用@SubComponent標記子component
使用SubComponent進行擴展,父component顯示的拋出依賴,需要持有子component對象。
通過父Component拿到子Component
注意兩個地方
1.AppComponent 是根Component。
2.UserComponent 是子Component,UserComponent是通過appComponent得到。
這就是通過Component的相互依賴做到的。
1、UserComponent
使用@Subcomponent標注
@UserScope
@Subcomponent(modules = UserModule.class)
public interface UserComponent {
AComponent plus(AModule aModule);
}
2、父Component
提供子Component。(返回值為子Component)
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
UserComponent plus(UserModule userModule);
}
三、@Name注解:區分不同對象的實例
該注解用來標識具體需要哪一個依賴。因為往往我們想要的對象不只有一種依賴方式。例如我們需要一個網絡請求庫,可能有提供緩存的,可能不需要緩存,那么這個時候我們在Moudle中就會提供兩種依賴。具體選擇哪個則需要通過name標識去取。
例如:
(http://www.lxweimin.com/writer#/notebooks/3267733/notes/4938210/preview)
1.Module提供兩種實例。
@Provides @Named("cached")@Singleton
OkHttpClient provideOkHttpClient(Cache cache) {
OkHttpClient client = new OkHttpClient();
client.setCache(cache);
return client;
}
@Provides
@Named("non_cached")
@Singleton
OkHttpClient provideOkHttpClient() {
OkHttpClient client = new OkHttpClient();
return client;
}
2.根據需求選取不同的實例
@Inject @Named("cached") OkHttpClient cache_client;
@Inject @Named("non_cached") OkHttpClient non_cache_client;
@Qualifier注解
當然Dagger2通過使用@Qualifier可以定制自己的Name形式注解。
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface UserNamed {
String value() default "";
}
這和Dagger2提供的name的代碼實現是一樣的
下面是Dagger2提供的name
http://chuansong.me/n/400687651115
Scope注解:用于限定范圍,指明Module可以像哪些對象提供依賴
1.定義自己Scope注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface UserScope {
}
2.在Module中加入Scope注解,表示為哪些對象提供實例
鴻洋
3.在Component中加入Scope注解,表示在這個Component中,有權利從Module中拿到被Scope標注的對象
@UserScope
@Subcomponent(modules = UserModule.class)
public interface UserComponent {
AComponent plus(AModule aModule);
BComponent plus(BModule bModule);
CComponent plus(CModule cModule);
}
參考資料
http://www.lxweimin.com/p/01d3c014b0b1
http://chuansong.me/n/400687651115
http://blog.csdn.net/study_zhxu/article/details/52169090
http://android.jobbole.com/82705/
http://gold.xitu.io/entry/572232fc1532bc00624b5c8e
http://codethink.me/2015/08/06/dependency-injection-with-dagger-2/非常簡潔清晰