- 目的
本文主要探討Dagger2的技術實作原理及使用方法,可以讓讀者快速上手Dagger2, - Dagger2介紹
2.1 Dagger2是什么?
Dagger2在Github主頁上的自我介紹是:“A fast dependency injector for Android and Java“(一個提供給Android和Java使用的快速依賴注射器,)
Dagger2是由谷歌接手開發,最早的版本Dagger1 是由Square公司開發的,
2.2 Dagger2相較于Dagger1的優勢是什么?
更好的性能:相較于Dagger1,它使用的預編譯期間生成代碼來完成依賴注入,而不是用的反射,反射對手機應用開發影響是比較大的,因為反射是在程式運行時加載類來進行處理所以會比較耗時,而手機硬體資源有限,所以相對來說會對性能產生一定的影響,
容易跟蹤除錯:因為dagger2是使用生成代碼來實作完整依賴注入,所以完全可以在相關代碼處下斷點進行運行除錯,
2.3 使用依賴注入的最大好處是什么?
使用依賴注入的最大好處就是模塊間解耦,就拿MVP來說,使用Dagger2可以將MVP中的V 層與P層進一步解耦,這樣便可以提高代碼的健壯性和可維護性,
如果在 Class A 中,有 Class B 的實體,則稱 Class A 對 Class B 有一個依賴,下面所說的依賴都是指類之間的依賴關系
如果不用Dagger2的情況下我們應該這么寫:
public class A {
...
B b;
...
public A() {
b = new B();
}
}
上面例子面臨著一個問題,一旦某一天B的創建方式(如構造引數)發生改變,那么你不但需要修改A中創建B的代碼,還要修改其他所有地方創建B的代碼,
如果我們使用了Dagger2的話,就不需要管這些了,只需要在需要B物件的地方寫下:
public class A {
@Inject
B b;
}
當然在使用@Inject注解的時候,需要其它一些代碼共同配合完成這個任務,下面將一一介紹Dagger2的功能,
3. Dagger2作業原理
我們先簡單的從整體上看一下Dagger2作業原理,在針對每個功能逐一講解,
上圖中,
1、提供依賴部分,就是new物件,有兩種方式,使用@Inject注解建構式或者提供一個Module類
2、@Component 它本質是一個使用@Component注解的介面,這個介面與Module類建立聯系,并提供inject()函式,
3、在原來需要使用new創建物件的地方,現在實作Component,然后呼叫它的inject()函式,
4、使用@Inject注解變數,就能生成它的物件,
- Dagger2中的注解
理解 Dagger2 中的幾個注解的含義和用法,是掌握 Dagger2 的關鍵,下面一個一個的分析,
? @Inject - @Inject 建構式
使用@Inject注解建構式,表示當創建類的實體時,Dagger會獲取類的建構式的引數,并呼叫此建構式創建實體,
class Thermosiphon implements Pump {
private final Heater heater;
@Inject
Thermosiphon(Heater heater) {
this.heater = heater;
}
...
}
- @Inject 變數
注解變數,就生成這個變數的實體,在這個例子中,它創建Heater實體和Pump實體,
class CoffeeMaker {
@Inject Heater heater;
@Inject Pump pump;
...
}
如果你的類中有@Inject 注解變數,但沒有@Inject注解建構式,Dagger將根據請求注入這些欄位,但不會創建新的實體, 可以使用@Inject注解一個無參建構式,以指示Dagger可以使用該無參建構式創建實體,
?
? @Provides
默認情況下,Dagger通過創建所請求型別的實體來滿足每個依賴關系,如上所述, 當你請求CoffeeMaker物件時,將通過呼叫new CoffeeMaker()創建實體,并將實體賦給CoffeeMaker變數,
但是@Inject不能在下面的地方作業:
- 無法創建實作了介面的類,
- 需要創建一個第三方類,無法使用@Inject注解這個類的建構式,也就無法使用@Inject來創建實體,
- 必須配置可配置物件!
對于@Inject不能滿足要求的情況下,使用@ Provide 注解一個函式來滿足依賴, 函式回傳的實體,就是需要的實體,
當需要創建Heater物件時,provideHeater()就會被呼叫
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
所有@Provides方法必須屬于一個模塊, 這里說的模塊是有@Module注解的類,如下所示:
@Module
class DripCoffeeModule {
@Provides static Heater provideHeater() {
return new ElectricHeater();
}
@Provides static Pump providePump(Thermosiphon pump) {
return pump;
}
}
? @Module
使用@Module注解的類,用來包含@Provides注解的一些方法
@Module
public class Module{
//A是第三方類別庫中的一個類
@Provides
A provideA(){
return A();
}
}
? @Component
- @Module 提供依賴, 就是提供生產類的實體
- @Inject注解函式表示提供依賴,@Inject注解變數表示請求依賴,
- @Component 就是聯系提供和請求這兩者的紐帶,
使用起來很簡單,只需要創建Component物件,然后呼叫Component的inject(A a)方法,就可以在類A中使用@Inject注解變數來創建實體,
創建Component物件時,需要創建他依賴的module和Component實體,見下面第2、3行代碼
mMainComponent = DaggerMainComponent.builder()
.appComponent(getAppComponent())
.mainModule(new MainModule())
.activityModule(new ActivityModule(this)).build();
Component的功能有哪些?或者說為什么要使用Component
1、可以表示哪個module來提供依賴
2、可以表示依賴哪些 Component
3、可以表示為誰提供依賴
4、可以表示提供哪些依賴
Component會首先從Module中查找可以創建實體的函式,若找到就用Module創建類實體,并停止查找Inject維度,否則才查找使用@Inject注解的建構式,所以創建類實體級別Module維度要高于Inject維度,
下面通過幾行代碼,來對@Component主要的功能進行介紹:
@Component 注解的是一個介面
比如下面這個介面,表示它可以提供一個 生產MyApplication 實體的依賴
@Singleton //這行先不去理會,下面會講到
@Component(modules = {AppModule.class},dependencies = {xxxComponent.class})
public interface AppComponent{
void inject(MyActivity myActivity);
public MyApplication getApplication();
}
對應上面說的四點:
1、modules = {AppModule.class}, 表示AppModule提供依賴,具體的函式實作是在AppModule里面,可以有多個,(用 @Module 注解的類)
2、dependencies = {xxxComponent.class},AppComponent中部分物件,需要xxxComponent.class來提供依賴,
3、有引數無回傳值的函式,表示這個類提供依賴,比如上面代碼中呼叫 inject(MyActivity myActivity),把AppComponent注入到MyActivity,那么在 MyActivity就可以通過@Inject 注解變數來生成實體了,
4、有回傳值無引數的函式表示AppComponent 向子Component(繼承AppComponent 的Component)暴露哪些依賴,這里可能會有疑問,Component 可提供的依賴不就是 Module 里的那些嗎?確實沒錯,這里的函式是父Component 對子Component暴露函式,并不一定會暴露 Module 中所有的方法,只有暴露的方法才能在子Component中使用,
?
5. 使用范例
5.1 關于Module和Component的使用
? 創建AppModule
@Module
public class AppModule {
Context context;
public AppModule(Context context){
this.context = context;
}
@Provides @Singleton
public Context provideContext(){
return context;
}
@Provides @Singleton
public ToastUtil provideToastUtil(){
return new ToastUtil(context);
}
}
? 創建AppComponent
@Singleton
@Component (modules={AppModule.class})// 由AppModule提供依賴
public interface AppComponent {
// 必須定義為介面,Dagger2框架將自動生成Component的實作類,對應的類名是Dagger×××××,這里對應的實作類是DaggerAppComponent
Context getContext();
ToastUtil getToastUtil();
}
通常約定AppComponent是一個全域的Component,負責管理整個App的全域類實體,當然你想怎么用都行,這只是一個主觀層面的規定,
Context getContext();和ToastUtils getToastUtils();是將Context和ToastUtils暴露給子Component,
在Application中創建AppComponent實體
注意下面的只是生成mAppComponent實體,并沒有呼叫inject();
public class App extends Application {
AppComponent mAppComponent;
@Override
public void onCreate() {
super.onCreate();
mAppComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
}
public AppComponent getAppComponent(){
return mAppComponent;
}
}
5.2 關于父Component中的方法暴露給子Component部分
現在來解釋一下Context getContext();和ToastUtils getToastUtils();是將Context和ToastUtils暴露給子Component,先看下面這段代碼
@PerActivity
@Component(dependencies = AppComponent.class,modules = {MainModule.class, ActivityModule.class})
public interface MainComponent extends ActivityComponent {
//對MainActivity進行依賴注入
void inject(MainActivity mainActivity);
}
下面是一個MainComponent的簡單使用
public class MainActivity extends BaseActivity implements MainFragment.OnFragmentInteractionListener {
private MainComponent mMainComponent;
@Inject
ToastUtil toastUtil;//注意這里的注解使用的是MainComponent依賴的AppComponent中暴露的ToastUtil getToastUtil();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Dagger2框架將自動生成MainComponent的實作類DaggerMainComponent
mMainComponent = DaggerMainComponent.builder().appComponent(getAppComponent())
.mainModule(new MainModule())
.activityModule(new ActivityModule(this)).build();
mMainComponent.inject(this);//一定不要忘記呼叫inject();
}
}
注意一定要呼叫inject()這個方法,我大概解釋一下,如果沒有呼叫inject(),那么toastUtil就是一個空值,
上面代碼中的第14行
mMainComponent = DaggerMainComponent.builder().appComponent(getAppComponent())
.mainModule(new MainModule())
.activityModule(new ActivityModule(this)).build();
這句代碼會生成使用@Inject 注解的變數的實體(也就是ToastUtil實體),會在mMainComponent實體中生成一個ToastUtil實體,
這里使用@Inject 注解的變數有什么要求呢??是不是隨便一個變數都能這樣去創建??
當然不是,需要MainComponent中的module 有提供這個依賴,或者依賴的AppComponent提供這個依賴(這里具體指的是 在AppComponent 中暴露出ToastUtil getToastUtil())
這個ToastUtil實體是有了 ,但是要和MainActivity實體中的toastUtil實體建立聯系就需要下面這行代碼
mMainComponent.inject(this);
這句代碼的意思是,將this實體與mMainComponent實體中的物件關聯起來,這樣this實體中的toastUtil就不是空值了,
6. Dagger2在android studio中的配置
只需要添加如下兩個dependencies:
dependencies {
implementation 'com.google.dagger:dagger-android:2.29.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.29.1'
}
版本號可以在如下鏈接中找到:
https://github.com/google/dagger/releases
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/197703.html
標籤:其他
