8年Android架構師:Dagger2使用詳解

原文連接:www.jianshu.com/p/2cd491f0d…java

目錄

  • 一:Dagger2是什麼?
  • 二:爲何要有Dagger2
  • 三:Dagger2如何使用
      1. 基本的概念
      1. 如何使用Dagger2
      1. 高級用法
      • (1)構造方法須要其餘參數時候
      • (2) 模塊之間的依賴關係
      • (3) @Named註解使用
      • (4) @Singleton註解
      • (5)自定義Scoped
      • (6)Subcomponent
      • (7)lazy 和 Provider
  • 四: MVP + Dagger2

一:Dagger2是什麼?

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

二:爲何要有Dagger2

下面我就手寫了git

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

使用的時候咱們就要github

A a = new A();
a.eat();
複製代碼

若是如今改了,早A的構造方法中必須傳入B對象api

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

那麼使用的時候bash

A a = new A(new B());
a.eat();
複製代碼

可能就有人說了,不就加一個對象麼,這裏只是我舉的一個很簡單的例子,看的感受很簡單,可是在實際開發中,若是如今改了一個這個構造方法。是否是意味着,整個項目中的都的改,一不當心, 就是BUG 啊架構

三:Dagger2如何使用

1. 基本的概念

上來給你說,怎麼玩,確定懵逼,這裏我簡單說一下幾個概念,想有個認知,在往下看,會好不少,Dagger 是經過@Inject使用具體的某個對象,這個對象呢,是由@Provides註解提供,可是呢,這個@Provides只能在固定的模塊中,也就是@Module註解,咱們查找的時候,不是直接去找模塊,而是去找@Componentapp

咱們反向推導,當咱們使用框架

@Inject
A a
複製代碼

想要獲取a對象的示例的時候,Dagger2 會先去找,當前Activity或者Fragment所鏈接的橋樑,例如上圖中,鏈接的只有一個橋樑,實際上能夠有多個,這個橋樑,會去尋找他所依賴的模塊,如圖中,依賴了模塊A,和模塊B,而後在模塊中,會去尋找@Providers註解,去尋找A的實例化對象。ide

2. 如何使用Dagger2

(1) 引入依賴庫

Dagger2官網

compile 'com.google.dagger:dagger:2.11'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.11'
複製代碼

(2) 建立Moudule

//第一步 添加@Module 註解
@Module
public class MainModule {
}
複製代碼

(3)建立具體的示例

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

(4)建立一個Component

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

(5)Rebuild Project

而後AS 會自動幫咱們生成一個

開頭都是以Dagger開始的

(6)將Component與Activity/Fragment綁定關係

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);

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

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

        /***
         * 第三步  調用A 對象的方法
         */
        a.eat();
    }
}
複製代碼

確定有小夥伴說了,爲了拿到一個對象,這麼大個彎,太麻煩了。別急慢慢看,路要一步一步走嘛

3. 高級用法

(1)構造方法須要其餘參數時候

怎麼說呢,就和最來時的意思同樣,

A a = new A(new B());
a.eat();
複製代碼

這種狀況,如何使用Dagger2呢 確定有小夥伴這麼想

@Provides
    A providerA() {
        return new A(new B());
    }
複製代碼

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

正確的打開方式

這時候,咱們什麼都不用改,只須要在moudule中添加一個依賴就能夠了

@Module
public class MainModule {

    /***
     * 構造方法須要其餘參數時候
     *
     * @return
     */
    @Provides
    B providerB() {
        return new B();
    }

    @Provides
    A providerA(B b) {
        return new A(b);
    }
}
複製代碼

(2) 模塊之間的依賴關係

模塊與模塊之間的聯繫

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

這樣的話,Dagger會如今A moudule 中尋找對象,若是沒找到,會去找module B 中是否有被Inject註解的對象,若是仍是沒有,那麼GG,拋出異常

一個Component 應用多個 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註解使用

至關於有個表示,雖然你們都是同一個對象,可是實例化對象不一樣就不如

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去掉試試

如今咱們完成了單例,而後作了一個事情,就是點擊某個按鈕,跳轉到一個新的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 ,都引用相同的對象,答應地址看看

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

/**
 * @做者 :  Android-SuperMan
 * @建立日期 :2017/7/14 下午3:04
 * @方法做用:
 * 參考Singleton 的寫法
 * Scope 標註是Scope
 * Documented 標記在文檔
 * @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;
    }
}
複製代碼

最後是如何使用

首先,這個是個橋樑,依賴方式,上文已經說過了

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

細心的小夥伴可能已經發現了,只有在上面一個MainComponent添加了一個@ActivityScoped,這裏說明一下,@Singleton是Application 的單例

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

因此,咱們這裏的@Singleton 級別大於咱們自定義的@ActivityScoped,同時,對應module 所依賴的component ,也要放上相應的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 若是是單例模式 對應的Compnent 也要是單例模式
  2. inject(Activity act) 不能放父類
  3. 即便使用了單例模式,在不一樣的Activity 對象仍是不同的
  4. 依賴component, component之間的Scoped 不能相同
  5. 子類component 依賴父類的component ,子類component的Scoped 要小於父類的Scoped,Singleton的級別是Application
  6. 多個Moudle 之間不能提供相同的對象實例
  7. Moudle 中使用了自定義的Scoped 那麼對應的Compnent 使用一樣的Scoped

(6)Subcomponent

這個是系統提供的一個Component,當使用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的時候拿到的對象都是同一個;而且當你第一次去get時,它纔會去初始化這個實例.

procider(強制加載)的做用: 1:同上當你第一次去get時,它纔會去初始化這個實例 2:後面當你去get這個實例時,是否爲同一個,取決於他Module裏實現的方式


四: MVP + Dagger2

這是如今主流的設計架構

MVP,這個我會在後面的文章介紹,這裏不作太多解釋

當你瞭解MVP 的時候,你就知道,全部的業務邏輯全在Presenter, 換句話, presenter 持有的對象,控制着你程序的所有邏輯,這在dagger 中,講白了 咱們只要將全部的presetner 對象控制就能夠了

下面附上目錄結構,固然僅僅做爲參考。dagger 強大的用法仍是須要各位本身去體會,下面的項目是我剛剛學會dagger 時候 寫的一個項目

能夠看到,我是將全部的activity 或者 fragment 所有添加在同一個Component中,固然如今的話不推薦,好比Utils 你能夠專門作一個Component,

首先放上個人Module,公司項目,不少東西沒敢放上來,體諒,能夠看到我這裏提供了一個SplashPresenter,也就是啓動頁的Presneter,業務邏輯

@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 :  Android-SuperMan
     * @ 建立日期 :  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;
    }

    .....
}
複製代碼

當我使用的時候,只須要注入便可,以下代碼

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);
    }
}
複製代碼

————————分割線————————

若是你看到了這裏,以爲文章寫得不錯就給個唄?若是你以爲那裏值得改進的,請給我留言。必定會認真查詢,修正不足。謝謝。

爲何某些人會一直比你優秀,是由於他自己就很優秀還一直在持續努力變得更優秀,而你是否是還在知足於現狀心裏在竊喜!但願讀到這的您能點個小贊關注下我,之後還會更新技術乾貨,謝謝您的支持!

轉發分享+關注,天天獲取更多資料

Android架構師之路很漫長,一塊兒共勉吧!

相關文章
相關標籤/搜索