Android - Dagger2使用詳解

Allens_JiangIP屬地: 安徽
10字數(shù) 2,260
老婆保佑,代碼無BUG

前言

2018年,大家開心哈,本文是在2018年第一天寫完的,歷時一年才完成,哈哈2018,大家加油


目錄

  • 一:Dagger2是什么?
  • 二:為什么要有Dagger2
  • 三:Dagger2如何使用
      1. 基本的概念
      1. 如何使用Dagger2
      1. 高級用法
      • (1)構(gòu)造方法需要其他參數(shù)時候
      • (2) 模塊之間的依賴關(guān)系
      • (3) @Named注解使用
      • (4) @Singleton注解
      • (5)自定義Scoped
      • (6)Subcomponent
      • (7)lazy 和 Provider
  • 四: MVP + Dagger2

一:Dagger2是什么?

是一個依賴注入框架,butterknife也是一個依賴注入框架。不過butterknife,最多叫奶油刀,Dagger2被叫做利器啊,他的主要作用,就是對象的管理,其目的是為了降低程序耦合。

二:為什么要有Dagger2

u=2303921310,3094597953&fm=27&gp=0.jpg

下面我就手寫了

public class A {
    public void eat() {
        System.out.print("吃飯了");
    }
}

使用的時候我們就要

A a = new A();
a.eat();

如果現(xiàn)在改了,早A的構(gòu)造方法中必須傳入B對象

public class A {
    private B b;
    public A(B b) {
        this.b = b;
    }
    public void eat() {
        System.out.print("吃飯了");
    }
}

那么使用的時候

A a = new A(new B());
a.eat();

可能就有人說了,不就加一個對象么,這里只是我舉的一個很簡單的例子,看的感覺很簡單,但是在實際開發(fā)中,如果現(xiàn)在改了一個這個構(gòu)造方法。是不是意味著,整個項目中的都的改,一不小心, 就是BUG 啊

額 呵呵

三:Dagger2如何使用

1. 基本的概念

上來給你說,怎么玩,肯定懵逼,這里我簡單說一下幾個概念,想有個認知,在往下看,會好很多,Dagger 是通過@Inject使用具體的某個對象,這個對象呢,是由@Provides注解提供,但是呢,這個@Provides只能在固定的模塊中,也就是@Module注解,我們查找的時候,不是直接去找模塊,而是去找@Component

屏幕快照 2017-12-29 下午1.51.18.png

我們反向推到,當(dāng)們使用

@Inject
A a

想要獲取a對象的示例的時候,Dagger2 會先去找,當(dāng)前Activity或者Fragment所連接的橋梁,例如上圖中,連接的只有一個橋梁,實際上可以有多個,這個橋梁,會去尋找他所依賴的模塊,如圖中,依賴了模塊A,和模塊B,然后在模塊中,會去尋找@Providers注解,去尋找A的實例化對象。

2. 如何使用Dagger2

(1) 引入依賴庫

Dagger2官網(wǎng)

    compile 'com.google.dagger:dagger:2.11'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11'

(2) 創(chuàng)建Moudule

//第一步 添加@Module 注解
@Module
public class MainModule {
}

(3)創(chuàng)建具體的示例

//第一步 添加@Module 注解
@Module
public class MainModule {
    //第二步 使用Provider 注解 實例化對象
    @Provides
    A providerA() {
        return new A();
    }
}

(4)創(chuàng)建一個Component

//第一步 添加@Component
//第二步 添加module
@Component(modules = {MainModule.class})
public interface MainComponent {
    //第三步  寫一個方法 綁定Activity /Fragment
    void inject(MainActivity activity);
}

(5)Rebuild Project

這一步是必須的

然后AS 會自動幫我們生成一個


屏幕快照 2017-12-30 下午12.53.05.png

開頭都是以Dagger開始的

(6)將Component與Activity/Fragment綁定關(guān)系

package com.allens.daggerdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import com.allens.daggerdemo.Bean.A;
import com.allens.daggerdemo.component.DaggerMainConponent;
import javax.inject.Inject;

public class MainActivity extends AppCompatActivity {
    /***
     * 第二步  使用Inject 注解,獲取到A 對象的實例
     */
    @Inject
    A a;

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

        /***
         * 第一步 添加依賴關(guān)系
         */
        //第一種方式
        DaggerMainConponent.create().inject(this);

        //第二種方式
        DaggerMainConponent.builder().build().inject(this);

        /***
         * 第三步  調(diào)用A 對象的方法
         */
        a.eat();
    }
}

肯定有小伙伴說了,為了拿到一個對象,這么大個彎,太麻煩了。別急慢慢看,路要一步一步走嘛

3. 高級用法

(1)構(gòu)造方法需要其他參數(shù)時候

怎么說呢,就和最來時的意思一樣,

A a = new A(new B());
a.eat();

這種情況,如何使用Dagger2呢
肯定有小伙伴這么想

   @Provides
    A providerA() {
        return new A(new B());
    }

直接 new 一個B ,這樣的使用方法,是不對的!!!!!!,不對的!!!!!!!,不對的!!!!!!!!!

正確的打開方式

這時候,我們什么都不用該,只需要在moudule中添加一個依賴就可以了

@Module
public class MainModule {

    /***
     * 構(gòu)造方法需要其他參數(shù)時候
     *
     * @return
     */
    @Provides
    B providerB() {
        return new B();
    }

    @Provides
    A providerA(B b) {
        return new A(b);
    }
}

(2) 模塊之間的依賴關(guān)系

屏幕快照 2017-12-30 下午1.28.11.png

模塊與模塊之間的聯(lián)系,

@Module (includes = {BModule.class})// includes 引入)
public class AModule {
    @Provides
    A providerA() {
        return new A();
    }
}

這樣的話,Dagger會現(xiàn)在A moudule 中尋找對象,如果沒找到,會去找module B 中是否有被Inject注解的對象,如果還是沒有,那么GG,拋出異常

一個Component 應(yīng)用多個 module

@Component(modules = {AModule.class,BModule.class})
public interface MainComponent {
    void inject(MainActivity activity);
}

dependencies 依賴其他Component

@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainConponent {
    void inject(MainActivity activity);
}

注意 這里有坑。一下會講解


(3) @Named注解使用

相當(dāng)于有個表示,雖然大家都是同一個對象,但是實例化對象不同就不如

A a1 = new A();
A a2 = new A();

// a1  a2 能一樣嘛

Module中 使用@Named注解

@Module
public class MainModule {

    private MainActivity activity;

    public MainModule(MainActivity activity) {
        this.activity = activity;
    }


    @Named("dev")
    @Provides
    MainApi provideMainApiDev(MainChildApi mainChildApi, String url) {
        return new MainApi(mainChildApi, activity,"dev");
    }
    
    @Named("release")
    @Provides
    MainApi provideMainApiRelease(MainChildApi mainChildApi, String url) {
        return new MainApi(mainChildApi, activity,"release");
    }


}

在Activity/Fragment中使用

public class MainActivity extends AppCompatActivity {

    @Named("dev")
    @Inject
    MainApi apiDev;

    @Named("release")
    @Inject
    MainApi apiRelease;

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

        DaggerMainComponent.builder()
                .mainModule(new MainModule(this))
                .mainChildModule(new MainChildModule())
                .build()
                .inject(this);
        apiDev.eat();
        apiRelease.eat();
        Log.i("TAG","apiDev--->" + apiDev);
        Log.i("TAG","apiRelease--->" + apiRelease);
    }

}

打印Log

07-14 01:46:01.170 2006-2006/? I/TAG: apiDev--->com.allen.rxjava.MainApi@477928f
07-14 01:46:01.170 2006-2006/? I/TAG: apiRelease--->com.allen.rxjava.MainApi@f2b291c

(4) @Singleton注解

單利模式,是不是超級方便,你想然哪個對象單利化,直接在他的Provider上添加@Singleton 就行了

例如

    @Singleton
    @Provides
    A providerA(B b) {
        return new A(b);
    }

注意: 第一個坑!!!
如果 moudule所依賴的Comonent 中有被單利的對象,那么Conponnent也必須是單利的

@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
}

然后 在Activity中使用,直接打印a1 a2 的地址,

    @Inject
    A a2;
    @Inject
    A a1;

可以看到Log

12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
12-30 01:32:58.420 3987-3987/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba

不相信的小伙伴可以吧@Singleton去掉試試

現(xiàn)在我們完成了單利,然后做了一個事情,就是點擊某個按鈕,跳轉(zhuǎn)到一個新的Activiry,兩邊都引用同樣一個A 對象,打印A 的地址,

說一下,一個Conponent 可以被對個Activity/Fragment 引用,如

@Singleton
@Component(modules = {MainModule.class})
public interface MainConponent {
    void inject(MainActivity activity);
    void inject(TestAct activity);
}

上面與兩個Activity, MainActivity 和 TestAct ,都引用相同的 對象,答應(yīng)地址看看

12-30 00:48:17.477 2788-2788/com.allens.daggerdemo E/TAG: A1---->com.allens.daggerdemo.Bean.A@11fa1ba
12-30 00:48:17.517 2788-2788/com.allens.daggerdemo E/TAG: A2---->com.allens.daggerdemo.Bean.A@4f81861

竟然不同,說好的單利呢

注意: 第二個坑,單利對象只能在同一個Activity中有效。不同的Activity 持有的對象不同

那有人就要問了,沒什么辦法么,我就想全局只要一個實例化對象啊? 辦法肯定是有的,


(5) 自定義Scoped

/**
 * @作者 :  allens
 * @創(chuàng)建日期 :2017/7/14 下午3:04
 * @方法作用:
 * 參考Singleton 的寫法
 * Scope 標(biāo)注是Scope
 * Documented 標(biāo)記在文檔
 * @Retention(RUNTIME) 運行時級別
 */
@Scope
@Documented
@Retention(RUNTIME)
public @interface ActivityScoped {
}

首先想一下,什么樣的對象,能夠做到全局單例,生命周期肯定和APP 綁定嘛,這里我做演示,一個AppAip 我們要對這個對象,全局單利,所以二話不說,先給Application 來個全家桶,

Module

@Module
public class AppModule {

    @Singleton
    @Provides
    AppApi providerAppApi() {
        return new AppApi();
    }
}

Component

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
    AppApi getAppApi();
}

Application

public class MyApp extends Application {

    private AppConponent appComponent;

    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppConpoment.create();
    }

    public AppConponent getAppComponent() {
        return appConponent;
    }
}

最后是如何使用

首先,這個是個橋梁,依賴方式,上文已經(jīng)說過了

@ActivityScoped
@Component(modules = {MainModule.class}, dependencies = AppConponent.class)
public interface MainComponent {
    void inject(MainActivity activity);
    void inject(TestAct activity);
}

細心的小伙伴可能已經(jīng)發(fā)現(xiàn)了,只有在上面一個MainComponent添加了一個@ActivityScoped,這里說明一下,@Singleton是Application 的單利

注意,第三個坑,子類component 依賴父類的component ,子類component的Scoped 要小于父類的Scoped,Singleton的級別是Application

所以,我們這里的@Singleton 級別大于我們自定義的@ActivityScoped,同時,對應(yīng)module 所依賴的component ,也要放上相應(yīng)的Scope

好吧,上面的例子,打印Log.

12-30 02:16:30.899 4717-4717/? E/TAG: A1---->com.allens.daggerdemo.Bean.AppApi@70bfc2
12-30 02:16:31.009 4717-4717/? E/TAG: A2---->com.allens.daggerdemo.Bean.AppApi@70bfc2

一樣啦

爬坑指南(極度重要)

  1. Provide 如果是單例模式 對應(yīng)的Compnent 也要是單例模式
  2. inject(Activity act) 不能放父類
  3. 即使使用了單利模式,在不同的Activity 對象還是不一樣的
  4. 依賴component, component之間的Scoped 不能相同
  5. 子類component 依賴父類的component ,子類component的Scoped 要小于父類的Scoped,Singleton的級別是Application
  6. 多個Moudle 之間不能提供相同的對象實例
  7. Moudle 中使用了自定義的Scoped 那么對應(yīng)的Compnent 使用同樣的Scoped

(6)Subcomponent

這個是系統(tǒng)提供的一個Component,當(dāng)使用Subcomponent,那么默認會依賴Component

例如

@Subcomponent(modules = TestSubModule.class)
public interface TestSubComponent {
    void inject(MainActivity activity);
}
@Component(modules = {MainModule.class})
public interface MainConponent {
    TestSubComponent add(TestSubModule module);
}

TestSubComponent中 我void inject(MainActivity activity);,便是這個橋梁,我是要注入到MainActivity,但是dagger 并不會給我生成一個Dagger開頭的DaggerTestSubComponent 這個類,如果我想使用TestSubModule.class里面提供的對象,依然還是使用DaggerMainConponent例如

   DaggerMainConponent
                .builder()
                .mainModule(new MainModule())
                .build()
                .add(new TestSubModule())
                .inject(this);

可以看到這里有一個add的方法,真是我在MainConponent添加的TestSubComponent add(TestSubModule module);


(7)lazy 和 Provider

public class Main3Activity extends AppCompatActivity {

    @PresentForContext
    @Inject
    Lazy<Present>     lazy;
    @PresentForName
    @Inject
    Provider<Present> provider;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main3);

        AppComponent appComponent = DaggerAppComponent.builder().appModule(new AppModule(this)).build();
        ActivityComponent activityComponent = DaggerActivityComponent.builder()
                .appComponent(appComponent)
                .activityModule(new ActivityModule())
                .build();

        activityComponent.injectActivity(this);
        Present present = lazy.get();
        Present present1 = provider.get();
    }
}

其中Lazy(懶加載)的作用好比component初始化了一個present對象,然后放到一個池子里,需要的時候就get它,所以你每次get的時候拿到的對象都是同一個;并且當(dāng)你第一次去get時,它才會去初始化這個實例.

procider(強制加載)的作用:
1:同上當(dāng)你第一次去get時,它才會去初始化這個實例
2:后面當(dāng)你去get這個實例時,是否為同一個,取決于他Module里實現(xiàn)的方式

參考

http://blog.csdn.net/qq_33994844/article/details/54972941


四: MVP + Dagger2

這是現(xiàn)在主流的設(shè)計架構(gòu)

MVP,這個我會在后面的文章介紹,這里不做太多解釋

當(dāng)你了解MVP 的時候,你就知道,所有的業(yè)務(wù)邏輯全在Presenter
換句話, presenter 持有的對象,控制著你程序的全部邏輯,這在dagger 中,講白了 我們只要將所有的presetner 對象控制就可以了

下面附上目錄結(jié)構(gòu),當(dāng)然僅僅作為參考。dagger 強大的用法還是需要各位自己去體會,下面的項目 是我剛剛學(xué)會dagger 時候 寫的一個項目

屏幕快照 2018-01-02 上午9.39.18.png
屏幕快照 2018-01-02 上午9.40.00.png

可以看到 ,我是 將所有的activity 或者 fragment 全部添加在同一個Component中,當(dāng)然現(xiàn)在的話 不推薦,比如Utils 你可以專門做一個Component,

首先放上我的Module,公司項目,很多東西沒敢放上來,體諒,可以看到 我這里提供了一個SplashPresenter,也就是啟動頁的Presneter,業(yè)務(wù)邏輯

@Module
public class ApiModule {

    public ApiModule() {

    }

    @Provides
    @Singleton
    Handler provideHandler() {
        return new Handler();
    }

    @Provides
    @Singleton
    SQLiteDatabase provideSQLiteDatabase() {
        return new DataBaseHelper(MyApp.context, Config.SqlName, null, Config.SqlVersion).getWritableDatabase();
    }

    /**
     * @ User :  allens
     * @ 創(chuàng)建日期 :  2017/7/13 下午3:24
     * @模塊作用 :
     * <p>
     * ====================================================================================================================================
     * ====================================================================================================================================
     */
    private SplashPresenter splashPresenter;

    public ApiModule(SplashAct splashAct) {
        splashPresenter = new SplashPresenter(splashAct, new SplashModel());
    }

    @Provides
    @Singleton
    SplashPresenter provideSplashPresenter() {
        return splashPresenter;
    }

    .....
}

當(dāng)我使用的時候,只需要注入即可,如下代碼

public class SplashAct extends BaseActivity implements SplashContract.View {


    @Inject
    SplashPresenter presenter;

    @Inject
    Handler handler;

    @Inject
    ApiService apiService;

    @Override
    protected void onCreate() {
        setContentView(R.layout.activity_splash);
    }

    @Override
    protected void initInject() {
        DaggerApiComponent.builder()
                .apiModule(new ApiModule(this))
                .build()
                .inject(this);
    }

    @Override
    protected void initListener() {
        presenter.getWordsInfo(true, apiService);
    }

    @Override
    public void gotoLogInAct() {
        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                startActivity(new Intent(SplashAct.this, LogInAct.class));
                finish();
            }
        }, 1500);
    }
}

©著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
132人點贊
Allens_Jiang分享,是進步最快的方式
總資產(chǎn)255共写了3.4W字获得812个赞共434个粉丝
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內(nèi)容