dagger2使用方法教程之簡明講解
前言
Dagger 這個庫的取名不僅僅來自它的本意“匕首”,同時也暗示了它的原理。Jake Wharton 在對 Dagger 的介紹中指出,Dagger 即 DAG-er,這里的 DAG 即數(shù)據(jù)結(jié)構(gòu)中的 DAG——有向無環(huán)圖(Directed Acyclic Graph)。也就是說,Dagger 是一個基于有向無環(huán)圖結(jié)構(gòu)的依賴注入庫,因此Dagger的使用過程中不能出現(xiàn)循環(huán)依賴。
Android開發(fā)從一開始的MVC框架,到MVP,到MVVM,不斷變化。現(xiàn)在MVVM的data-binding還在實驗階段,傳統(tǒng)的MVC框架Activity內(nèi)部可能包含大量的代碼,難以維護,現(xiàn)在主流的架構(gòu)還是使用MVP(Model + View + Presenter)的方式。但是 MVP 框架也有可能在Presenter中集中大量的代碼,引入DI框架Dagger2 可以實現(xiàn) Presenter 與 Activity 之間的解耦,Presenter和其它業(yè)務(wù)邏輯之間的解耦,提高模塊化和可維護性。
現(xiàn)在的公司項目用到了Dagger2,之前只是稍微了解一些,沒有用過,然后查了查資料,整理如下,方便快速上手
四個基本注解
1、@Inject 主要有兩個作用,一個是使用在構(gòu)造函數(shù)上,通過標記構(gòu)造函數(shù)讓Dagger2來使用(Dagger2通過Inject標記可以在需要這個類實例的時候來找到這個構(gòu)造函數(shù)并把相關(guān)實例new出來)從而提供依賴,另一個作用就是標記在需要依賴的變量讓Dagger2為其提供依賴。
@Inject注解的字段不能是private和protected的
2、@Module 用Module標注的類是專門用來提供依賴的。有的人可能有些疑惑,看了上面的@Inject,需要在構(gòu)造函數(shù)上標記才能提供依賴,那么如果我們需要提供的類構(gòu)造函數(shù)無法修改怎么辦,比如一些jar包里的類,我們無法修改源碼。這時候就需要使用Module了。Module可以給不能修改源碼的類提供依賴,當然,能用Inject標注的通過Module也可以提供依賴。
這里需要注意,Module和Inject這兩個注解還是有區(qū)別的,@Inject使用在構(gòu)造函數(shù)上的時候,這個構(gòu)造函數(shù)有沒有參數(shù)都可以,如果有參數(shù)的話這個Module也需要有其他Module或者@Inject構(gòu)造函數(shù)提供實例,適合在提供該類自己的時候使用。但是如果用@Module的話,@Module注解的這個類需要有默認無參構(gòu)造函數(shù)(顯示隱式都可以),否則會報“”xxx must be set”。如果沒有默認無參構(gòu)造函數(shù),就需要手動把這個Module的實例傳入Component,一般在MVP模式里使用該方式,用來提供Activity實例給Presenter實例。
所以,如果該類只需要提供自己,建議直接使用@Inject函數(shù),如果是用來提供其他類的實例,建議使用@Module的方式。
3、@Provides 用Provides來標注一個方法,該方法可以在需要提供依賴時被調(diào)用,從而把預先提供好的對象當做依賴給標注了@Inject的變量賦值。provides主要用于標注Module里的方法。
4、@Component 一般用來標注接口,被標注了Component的接口在編譯時會產(chǎn)生相應(yīng)的類的實例來作為提供依賴方和需要依賴方之間的橋梁,把相關(guān)依賴注入到其中。
四個擴展注解
1、@Qulifier 這里有個概念,叫依賴迷失,就是在Module注解的類里,有2個Provides都提供某個類的實例,這時候不用@Qulifier注解的話Component會不知道用哪個實例,這時候就要使用@Qulifier,下面直接提供代碼
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface A {}
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface B {}
@Module
public class SimpleModule {
@Provides
@A
Cooker provideCookerA(){
return new Cooker("James","Espresso");
}
@Provides
@B
Cooker provideCookerB(){
return new Cooker("Karry","Machiato");
}
}
public class ComplexMaker implements CoffeeMaker {
Cooker cookerA;
Cooker cookerB;
@Inject
public ComplexMaker(@A Cooker cookerA,@B Cooker cookerB){
this.cookerA = cookerA;
this.cookerB = cookerB;
}
}
2、@Named 和@Qulifier一樣,并且@Named就是繼承@Qulifier的,而且用起來比@Qulifier方便,示例代碼如下:
@Module
public class MainModule {
@Provides
@Named("red")
public Cloth getRedCloth() {
Cloth cloth = new Cloth();
cloth.setColor("紅色");
return cloth;
}
@Provides
@Named("blue")
public Cloth getBlueCloth() {
Cloth cloth = new Cloth();
cloth.setColor("藍色");
return cloth;
}
@Provides
public Clothes getClothes(@Named("blue") Cloth cloth){
return new Clothes(cloth);
}
}
public class MainActivity extends AppCompatActivity {
...
@Inject
@Named("red")
Cloth redCloth;
@Inject
@Named("blue")
Cloth blueCloth;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
tv.setText("我現(xiàn)在有" + redCloth + "和" + blueCloth );
}
}
3、@Scope 局部單例,意思就是在被注入類里只有一個該類的實例,局部范圍是啥,那就是它生命周期范圍內(nèi)。直接上代碼
//PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {}
//ActivityModule.java
@Module
public class ActivityModule {
@Provides
CoffeeShop provideCoffeeShop(){
return CoffeeShop.getInstance();//一個普通的單例
}
/**
* 直接在這里說結(jié)果,@PerActivity是用@Scope注解的,除了在這里注解還需要在用到該Module類的Component的類名上方也要注解,然后該實例在注入到某個類里的時候用同一個Component就會不管有幾個字段都會只有一個實例。注意:如果用不同的Component實例的話仍然會新的CookerFactory實例,單例CookerFactory只存在一個Component實例里。所以叫局部單例。
*/
@Provides
@PerActivity
CookerFactory provideCookerFactory(){
return new CookerFactory();
}
@Provides
CookerFactoryMulty provideCookerFactoryMulty(){
return new CookerFactoryMulty();//非單例
}
}
//CoffeeShop.java
public class CoffeeShop {
private static CoffeeShop INSTANCE;
private CoffeeShop(){
Log.d("TAG","CoffeeShop New Instance");
}
public static CoffeeShop getInstance(){
if(INSTANCE == null){
INSTANCE = new CoffeeShop();
}
return INSTANCE;
}
}
//CookerFactory.java
public class CookerFactory {
public CookerFactory(){
Log.d("TAG","CookerFactory New Instance");
}
}
//CookerFactoryMulty.java
public class CookerFactoryMulty {
public CookerFactoryMulty(){
Log.d("TAG","CookerFactoryMulty New Instance");
}
}
//除了在Module的Provides方法里寫上@Scope還需要在Component類名上方寫上,這里自定義的@Scope名字叫PerActivity
@PerActivity
@Component(modules = {ActivityModule.class})
public interface ActivityComponent {
void inject(MainActivity simpleActivity);
}
public class MainActivity extends Activity {
ActivityComponent activityComponent;
@Inject
CoffeeShop coffeeShop1;
@Inject
CoffeeShop coffeeShop2;
@Inject
CookerFactory cookerFactory1;
@Inject
CookerFactory cookerFactory2;
@Inject
CookerFactoryMulty cookerFactoryMulty1;
@Inject
CookerFactoryMulty cookerFactoryMulty2;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
activityComponent = DaggerActivityComponent.builder()
//下面這句話可以不寫,因為Module有默認構(gòu)造函數(shù)。如果Module的構(gòu)造器里有參數(shù),并且該參數(shù)不是注入進去的,就需要用類似下面的方法手動設(shè)置實例到Component中
.activityModule(provideModule())
.applicationComponent(MyApplication.getComponent()).build();
activityComponent.inject(this);
coffeeFactory.run();
}
private ActivityModule provideModule(){
return new ActivityModule();
}
}
運行結(jié)果
07-11 16:53:27.978 1927-1927/? D/TAG﹕ CoffeeShop New Instance 07-11 16:53:27.978 1927-1927/? D/TAG﹕ CookerFactory New Instance 07-11 16:53:27.978 1927-1927/? D/TAG﹕ CookerFactoryMulty New Instance 07-11 16:53:27.978 1927-1927/? D/TAG﹕ CookerFactoryMulty New Instance
@Singleton 該注解繼承@Scope,用的時候區(qū)別就是不用去自定義@Scope了,比如上面定義@PerActivity的這步就不需要了,其他的用法和使用@PerActivity一模一樣,也是在Component類名上面和Module的Provides方法里都寫上注解。
注意注意注意:再次提醒,局部單例是在同一個Component實例提供依賴的前提下才有效的,不同的Component實例只能通過Component依賴才能實現(xiàn)單例。也就是說,你雖然在兩個Component接口上都添加了PerActivity注解或者Singleton注解,但是這兩個Component提供依賴時是沒有聯(lián)系的,他們只能在各自的范圍內(nèi)實現(xiàn)單例
在@Inject標注的構(gòu)造器上使用局部單例直接在類名上聲明作用范圍(類名上添加@Singleton或自定義Scope)
依賴:dependencies
Component依賴Component的情況下,兩個Component的@Scope不能相同,否則會編譯錯誤,為什么這么設(shè)計我還不是很清楚,有知道的小伙伴請告訴我謝謝。
依賴的示例代碼如下:
//Person.java
public class Person {
}
//PerActivity.java
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerActivity {}
//BaseModule.java
@Module
public class BaseModule {
@Singleton
@Provides
public Person providePerson(){
return new Person();
}
}
//Module1.java
@Module
public class Module1 {
}
//Module2.java
@Module
public class Module2 {
}
//BaseComponent.java
@Singleton
@Component(modules = BaseModule.class)
public interface BaseComponent {
public Person providePerson();
}
//Component1.java
@PerActivity
//@Singleton
//因為依賴(dependencies)的BaseComponent中用到了@Singleton,所以這個Component就不能再用了,否則會編譯錯誤,為什么這么設(shè)計還不是很清楚
@Component(modules = Module1.class,dependencies = BaseComponent.class)
public interface Component1 {
void inject(TestScopeActivity1 simpleActivity);
}
//Component2.java
@PerActivity
//@Singleton 為什么不能用?原理同上
@Component(modules = Module2.class,dependencies = BaseComponent.class)
public interface Component2 {
void inject(TestScopeActivity2 simpleActivity);
}
//MyApplication.java
public class MyApplication extends Application {
private static BaseComponent baseComponent;
@Override
public void onCreate() {
super.onCreate();
baseComponent = DaggerBaseComponent.builder().build();
}
public static BaseComponent getBaseComponent() {
return baseComponent;
}
}
//TestScopeActivity1.java
public class TestScopeActivity1 extends Activity {
@Inject
Person p = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerComponent1.builder().baseComponent(MyApplication.getBaseComponent()).build().inject(this);
TextView textView = findViewById(R.id.textView);
textView.setText(p.toString());
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(TestScopeActivity1.this,TestScopeActivity2.class));
}
});
}
}
//TestScopeActivity2.java
public class TestScopeActivity2 extends Activity {
@Inject
Person p = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DaggerComponent2.builder().baseComponent(MyApplication.getBaseComponent()).build().inject(this);
TextView textView = findViewById(R.id.textView);
textView.setText(p.toString());
}
}
daggar2如何選擇依賴呢,按照這樣的順序
當Component調(diào)用inject方法的時候,會搜索被注入類中用@Inject注解的字段,然后會在該Component中查找在@Component(modules=。。。)注解中注冊的Module,如果搜索到Module有@Provides注解的方法提供該@Inject注解的字段所需的實例,就調(diào)用相應(yīng)的方法完成注入,否則就查找所有用@Inject注解構(gòu)造函數(shù)的類,如果找到就調(diào)用相應(yīng)的構(gòu)造函數(shù)完成注入,如果在獲得實例的時候還需要獲取參數(shù)的實例,再按照剛才的流程依次注入?yún)?shù)實例
畫個簡單的流程,如下所示
Component.inject->在Component搜索Module->找到就調(diào)用@Provides注解的方法提供實例
->沒找到就搜索@Inject注解的構(gòu)造函數(shù)
->都找不到就報錯。。。
都沒找到肯定就報錯了。。。但是會優(yōu)先尋找Component注冊的Module,而@Inject注冊的構(gòu)造器可以調(diào)用任何Component的inject方法完成注入,因為@Inject注冊的構(gòu)造器不需要在Component里注冊,這里和Module有區(qū)別,Module是需要在某個Component中注冊的,而@Inject不需要
舉一個MVP中使用Dagger2的示例,我就不貼代碼了,直接看下面這個鏈接好了:http://www.dbjr.com.cn/article/138093.htm
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。
相關(guān)文章
Android把svg圖片轉(zhuǎn)為jpg保存到相冊圖庫
這篇文章主要為大家詳細介紹了Android把svg圖片轉(zhuǎn)為jpg保存到相冊圖庫,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-05-05
Android實現(xiàn)左上角(其他邊角)傾斜的標簽(環(huán)繞效果)效果
這篇文章主要介紹了Android實現(xiàn)左上角(其他邊角)傾斜的標簽(環(huán)繞效果)效果,本文通過圖文實例代碼相結(jié)合的形式給大家介紹的非常詳細,具有一定的參考借鑒價值,需要的朋友可以參考下2019-10-10
Android LayoutTransiton實現(xiàn)簡單的錄制按鈕
這篇文章主要介紹了Android LayoutTransiton實現(xiàn)簡單的錄制按鈕,主要實現(xiàn)開始,暫停,停止和顯示錄制時間長度,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06
Android組件創(chuàng)建DrawerLayout導航
這篇文章主要為大家詳細介紹了Android組件創(chuàng)建DrawerLayout導航的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01
Android使用Profiler查看應(yīng)用內(nèi)存分析的操作步驟
內(nèi)存分析是Profiler中的一個組件,可以幫助我們識別可能會導致應(yīng)用卡頓、凍結(jié)甚至崩潰的內(nèi)存泄露和內(nèi)存抖動,本文小編將給大家介紹一下Android使用Profiler查看應(yīng)用內(nèi)存分析的操作步驟,需要的朋友可以參考下2023-10-10

