Rx系列<第三十四篇>:Android開(kāi)發(fā)需要了解的Dagger2

在Android中Dagger2主要目的是解耦,一般結(jié)合MVP達(dá)到完全解耦的效果。

(1)什么是Dagger2?

Dagger是為Android和Java平臺(tái)提供的一個(gè)完全靜態(tài)的,在編譯時(shí)進(jìn)行依賴(lài)注入的框架,原來(lái)是由Square公司維護(hù),現(xiàn)在由Google維護(hù)。

(2)存在的意義

我們?cè)赼ctivity中有可能會(huì)用到很多很多的類(lèi),這些類(lèi)要在activity中進(jìn)行實(shí)例化,這樣就導(dǎo)致我們的activity非常依賴(lài)這么多的類(lèi),這樣的程序耦合非常嚴(yán)重,不便于維護(hù)和擴(kuò)展,有什么辦法可以不去依賴(lài)這些類(lèi)呢,這時(shí)候就需要有一個(gè)容器,將這些類(lèi)放到這個(gè)容器里并實(shí)例化,我們activity在用到的時(shí)候去容器里面取就可以了,我們從依賴(lài)類(lèi)到依賴(lài)這個(gè)容器,實(shí)現(xiàn)了解耦,這就是我所理解的依賴(lài)注入,即所謂控制反轉(zhuǎn)。

(3)注解之@Inject

主要有兩個(gè)作用,一個(gè)是使用在構(gòu)造函數(shù)上,通過(guò)標(biāo)記構(gòu)造函數(shù)讓Dagger2來(lái)使用(Dagger2通過(guò)Inject標(biāo)記可以在需要這個(gè)類(lèi)實(shí) 例的時(shí)候來(lái)找到這個(gè)構(gòu)造函數(shù)并把相關(guān)實(shí)例new出來(lái))從而提供依賴(lài),另一個(gè)作用就是標(biāo)記在需要依賴(lài)的變量讓Dagger2為其提供依賴(lài)。

(4)注解之@Module

用Module標(biāo)注的類(lèi)是專(zhuān)門(mén)用來(lái)提供依賴(lài)的。有的人可能有些疑惑,看了上面的@Inject,需要在構(gòu)造函數(shù)上標(biāo)記才能提供依賴(lài),那么如果我們需要提供的類(lèi)構(gòu)造函數(shù)無(wú)法修改怎么辦,比如一些jar包里的類(lèi),我們無(wú)法修改源碼。這時(shí)候就需要使用Module了。Module可以給不能修改源碼的類(lèi)提供依賴(lài),當(dāng)然,能用Inject標(biāo)注的通過(guò)Module也可以提供依賴(lài)

(5)注解之@Provides

用Provide來(lái)標(biāo)注一個(gè)方法,該方法可以在需要提供依賴(lài)時(shí)被調(diào)用,從而把預(yù)先提供好的對(duì)象當(dāng)做依賴(lài)給標(biāo)注了@Inject的變量賦值。provide主要用于標(biāo)注Module里的方法

(6)注解之@Component

Component一般用來(lái)標(biāo)注接口,被標(biāo)注了Component的接口在編譯時(shí)會(huì)產(chǎn)生相應(yīng)的類(lèi)的實(shí)例來(lái)作為提供依賴(lài)方和需要依賴(lài)方之間的橋梁,把相關(guān)依賴(lài)注入到其中。

(7)添加依賴(lài)
implementation 'com.google.dagger:dagger:2.22.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
(8)@Inject和@Component使用

我們先看一下未注入Dagger的代碼

public class MainActivity extends BaseActivity {

    private DaggerTest daggerTest;

    @SuppressLint("CheckResult")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        daggerTest = new DaggerTest();

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest.print();
            }
        });

    }
}


public class DaggerTest {

    /**
     * 打印測(cè)試數(shù)據(jù)
     */
    public void print(){
        Log.d("aaa", "做一個(gè)小測(cè)試");
    }
}

在MainActivity中創(chuàng)建一個(gè)DaggerTest對(duì)象,這樣的話MainActivity和DaggerTest就產(chǎn)生了依賴(lài)關(guān)系,這就產(chǎn)生了耦合。下面將使用Dagger來(lái)解耦:

第一步 在MainActivity中使用@Inject注解

public class MainActivity extends BaseActivity {

    @Inject
    DaggerTest daggerTest;

    @SuppressLint("CheckResult")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest.print();
            }
        });

    }
}

第二步 在DaggerTest的構(gòu)造方法中使用@Inject注解(構(gòu)造方法不可以是private)

public class DaggerTest {

    @Inject
    public DaggerTest(){

    }

    /**
     * 打印測(cè)試數(shù)據(jù)
     */
    public void print(){
        Log.d("aaa", "做一個(gè)小測(cè)試");
    }

}

第三步 新建一個(gè)TestComponent接口,并用@Component修飾

Component是連接注入類(lèi)和目標(biāo)類(lèi)的橋梁。

@Component
public interface TestComponent {

    void inject(MainActivity mainActivity);

}

Component會(huì)查找目標(biāo)類(lèi)中用Inject注解標(biāo)注的屬性,查找到相應(yīng)的屬性后會(huì)接著查找該屬性對(duì)應(yīng)的用Inject標(biāo)注的構(gòu)造函數(shù)

第四步 將目標(biāo)類(lèi)注入到Component中

我們?cè)贛ainActivity中的onCreate方法中添加以下代碼

    DaggerTestComponent.create().inject(this);

這種方式的局限性:

  • DaggerTest構(gòu)造方法不可以帶有參數(shù);
  • DaggerTest不可以存在多個(gè)構(gòu)造方法;
(9)解決第(8)項(xiàng)構(gòu)造方法中不能帶參數(shù)的問(wèn)題

要想構(gòu)造方法中可以帶有參數(shù),需要@Module和@Provides的支持。

第一步 在MainActivity中使用@Inject注解

public class MainActivity extends BaseActivity {

    @Inject
    DaggerTest daggerTest;

    @SuppressLint("CheckResult")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest.print();
            }
        });

    }
}

第二步 在DaggerTest的構(gòu)造方法中使用@Inject注解

public class DaggerTest {

    @Inject
    public DaggerTest(String s){

    }

    /**
     * 打印測(cè)試數(shù)據(jù)
     */
    public void print(){
        Log.d("aaa", "做一個(gè)小測(cè)試");
    }

}

第三步 新建Model

@Module
public class TestModel {

    @Provides
    public DaggerTest provideDaggerTest(){
        return new DaggerTest("a");
    }

}

Model類(lèi)使用@Module修飾,@Provides修飾任意方法并提供對(duì)象的實(shí)例。

第四步 新建一個(gè)TestComponent接口,用@Component修飾,并指定某Model

@Component(modules = TestModel.class)
public interface TestComponent {

    void inject(MainActivity mainActivity);

}

第五步 將目標(biāo)類(lèi)注入到Component中

我們?cè)贛ainActivity中的onCreate方法中添加以下代碼

    DaggerTestComponent.builder()
            .testModel(new TestModel())
            .build()
            .inject(this);

當(dāng)然TestModel的構(gòu)造方法可以帶有參數(shù)

    DaggerTestComponent.builder()
            .testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
            .build()
            .inject(this);

這種方式的局限性:

  • DaggerTest不可以存在多個(gè)構(gòu)造方法;
(10)注入類(lèi)擁有多個(gè)構(gòu)造方法的情況

使用@Named可以使構(gòu)造方法分類(lèi),我就不分步驟了,直接貼一下代碼:

public class MainActivity extends BaseActivity {

    @Named("type1")
    @Inject
    DaggerTest daggerTest1;

    @Named("type2")
    @Inject
    DaggerTest daggerTest2;

    @Named("type3")
    @Inject
    DaggerTest daggerTest3;

    @SuppressLint("CheckResult")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        DaggerTestComponent.builder()
                .testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
                .build()
                .inject(this);


        findViewById(R.id.bt_1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                daggerTest1.print();
                daggerTest2.print();
                daggerTest3.print();
            }
        });

    }
}


public class DaggerTest {

    private String printStr = "";

    public DaggerTest(){

    }

    public DaggerTest(String s){
        printStr = s;
    }

    public DaggerTest(String s, String ss){
        printStr = s + ss;
    }

    /**
     * 打印測(cè)試數(shù)據(jù)
     */
    public void print(){
        if(!TextUtils.isEmpty(printStr)){
            Log.d("aaa", printStr);
        }else{
            Log.d("aaa", "做一個(gè)小測(cè)試");
        }
    }
}


@Module
public class TestModel {

    private String test;

    public TestModel(String s){
        test = s;
    }

    @Named("type1")
    @Provides
    public DaggerTest provideDaggerTest1(){
        return new DaggerTest();
    }

    @Named("type2")
    @Provides
    public DaggerTest provideDaggerTest2(){
        return new DaggerTest(test);
    }

    @Named("type3")
    @Provides
    public DaggerTest provideDaggerTest3(){
        return new DaggerTest(test, "1");
    }
}


@Component(modules = TestModel.class)
public interface TestComponent {

    void inject(MainActivity mainActivity);

}
(11)自定義注解替代@Named

@Qualifier注解可以解決注入類(lèi)擁有多個(gè)構(gòu)造方法時(shí)依賴(lài)沖突的問(wèn)題,下面我們自定義一個(gè)注解@CustomName。

@Qualifier
@Documented
@Target({ ElementType.FIELD, ElementType.METHOD })
@Retention(RUNTIME)
public @interface CustomName {

    String value() default "";

}

我們只要將(10)中的@Named換成@CustomName即可。

(12)單例模式

使用@Singleton注解實(shí)現(xiàn)單例。

第一步 在Component中添加@Singleton

@Singleton
@Component(modules = TestModel.class)
public interface TestComponent {

    void inject(MainActivity mainActivity);

}

第二步 在Model中的被@Provides修飾的方法上添加@Singleton

@Singleton
@Provides
public DaggerTest provideDaggerTest1(){
    return new DaggerTest();
}

以上兩部可以實(shí)現(xiàn)局部單例。

當(dāng)我們從各種Activity中都使用了以下代碼

    DaggerTestComponent.builder()
            .testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
            .build()
            .inject(this);

那么DaggerTest只在各個(gè)Activity中是單例的,不同Activity中的DaggerTest的hashcode不同?,F(xiàn)在我們將局部單例改造成全局單例

  • 自定義Application
public class MyApp extends Application {

    private static TestComponent testComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        testComponent = DaggerTestComponent.builder()
                .testModel(new TestModel("我傳遞了一個(gè)數(shù)據(jù)"))
                .build();
    }

    public static TestComponent getTestComponent() {
        return testComponent;
    }

}

public static TestComponent getTestComponent() {
    return testComponent;
}
  • 在Activity引用Activity中的Component對(duì)象

      MyApp.getTest().inject(this);
    
    (13)自定義Scope

自定義Scope比較簡(jiǎn)單,現(xiàn)在我們想定義一個(gè)@MyScope注解,代碼如下

@Scope
@Documented
@Retention(RUNTIME)
public @interface MyScope {

}

@Singleton則是@Scope的默認(rèn)實(shí)現(xiàn),所以也可以寫(xiě)成

@Scope
@Documented
@Retention(RUNTIME)
public @interface Singleton{

}
(14)延遲注入(Lazy)

有時(shí)我們想注入的依賴(lài)在使用時(shí)再完成初始化,加快加載速度,就可以使用注入Lazy<T>。只有在調(diào)用 Lazy<T>get() 方法時(shí)才會(huì)初始化依賴(lài)實(shí)例注入依賴(lài)。

@Inject
Lazy<DaggerTest> daggerTest1;


daggerTest1.get().print(String.valueOf(daggerTest1.hashCode()));
(15)多個(gè)依賴(lài)的情況

假設(shè)有A,B兩個(gè)類(lèi),在A中新建一個(gè)B對(duì)象,那么A和B就產(chǎn)生了依賴(lài),我們稱(chēng)之為:A依賴(lài)于B。

結(jié)合上面講解到的知識(shí),我們很容易就會(huì)想到如何使用Dagger來(lái)解耦:

第一步 在A中使用@Inject修飾B的成員變量

@Inject
A a;

第二步 使用@Module@Provides新建一個(gè)Model

@Module
public class BModel {

    @Provides
    public DaggerTest provideDaggerB(){
        return new B();
    }

} 

第三步 使用@Component定義接口,這個(gè)接口作為A和B形成依賴(lài)關(guān)系的橋梁

@Component(modules = BModel.class)
public interface ABComponent {

    void inject(A a);

}

第四步 在A中使用以下代碼,即可完成A和B的依賴(lài)關(guān)系。

   ABComponent abComponent = DaggerABComponent.builder()
            .testModel(new BModel())
            .build()
            .inject(this);

以上四個(gè)步驟比較簡(jiǎn)單,但是這時(shí)又來(lái)了一個(gè)C類(lèi),在C類(lèi)中新建一個(gè)B對(duì)象,那么C和B就產(chǎn)生了依賴(lài),我們稱(chēng)之為C依賴(lài)于B。

我們可以在@Component新增接口可以實(shí)現(xiàn)

@Component(modules = BModel.class)
public interface ABComponent {

    void inject(A a);

    void inject(C c);

}
(16)一個(gè)橋梁可以引用多個(gè)modules
@Component(modules = {TestModel.class, Test2Model.class})
public interface TestComponent {

    void inject(MainActivity mainActivity);

}
(17)Component的依賴(lài)關(guān)系

Component就是橋梁,如果某橋梁想要使用另一個(gè)橋梁的方法時(shí),可通過(guò)依賴(lài)實(shí)現(xiàn)。

第一步 使用@Inject聲明成員變量

@Inject
DaggerTest2 daggerTest2;

第二步 新建Modules

@Module
public class Test2Model {

    @Provides
    public DaggerTest2 provideDaggerTest1(){
        return new DaggerTest2();
    }
}

第三步 橋梁

@Component(modules = {TestModel.class})
public interface TestComponent {

    void inject(MainActivity mainActivity);

    DaggerTest getDaggerTest();

}


@Component(modules = Test2Model.class, dependencies = TestComponent.class)
public interface Test2Component {

    void test(DemoActivity mainActivity);

}

第四步 產(chǎn)生依賴(lài),并調(diào)用依賴(lài)橋梁的方法

    DaggerTest daggerTest = DaggerTestComponent.create().getDaggerTest();
    daggerTest.print("aaaa");

    DaggerTest2Component.builder()
            .testComponent(DaggerTestComponent.create())
            .test2Model(new Test2Model())
            .build()
            .test(this);
(18)可參考的其它博客

Dagger 2 完全解析(一),Dagger 2 的基本使用與原理
Dagger 2 完全解析(二),進(jìn)階使用 Lazy、Qualifier、Scope 等
Dagger 2 完全解析(三),Component 的組織關(guān)系與 SubComponent
Dagger 2 完全解析(四),Android 中使用 Dagger 2
Dagger 2 完全解析(五),Kotlin 中使用 Dagger 2
Dagger 2 完全解析(六),dagger.android 擴(kuò)展庫(kù)的使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Dagger2 轉(zhuǎn)載請(qǐng)注明原作者,如果你覺(jué)得這篇文章對(duì)你有幫助或啟發(fā),可以關(guān)注打賞。 前言本文翻譯自Google ...
    輕云時(shí)解被占用了閱讀 6,931評(píng)論 4 31
  • 前言 ??Dagger是幫助實(shí)現(xiàn)依賴(lài)注入的庫(kù),雖然很多人都知道依賴(lài)注入對(duì)于架構(gòu)設(shè)計(jì)的重要性,但是Dagger學(xué)習(xí)曲...
    申國(guó)駿閱讀 2,982評(píng)論 5 9
  • 原文地址Dagger2 入門(mén),以初學(xué)者角度 依賴(lài)注入 Dagger2是Android中比較熱門(mén)的依賴(lài)注入框架,什么...
    Marlon_IT閱讀 1,361評(píng)論 1 16
  • 文章翻譯自Dagger官網(wǎng),翻譯水平有限,見(jiàn)諒。 引入 引用官網(wǎng)的引入說(shuō)明,上面的部分都好理解,就是簡(jiǎn)單的comp...
    one_cup閱讀 693評(píng)論 0 0
  • 雨, 零落一夜寒意 牽手夜風(fēng)徐徐, 偷跑進(jìn)戀人的外衣, 肆意妄為深秋的情緒, 阻隔戀人相依。 看他們各自的士, 慌...
    羅子閱讀 1,412評(píng)論 60 96

友情鏈接更多精彩內(nèi)容