Randall | 二逾苫、Dagge2

一、依賴注入是什么枚钓?

曾幾何時(shí)铅搓,項(xiàng)目中每個(gè)依賴單例Manager的地方,getInstance()方法是必不可少的搀捷。

public class AccountManager {
  private static class Holder {
    private static final AccountManager instance = new AccountManager();
  }
  public static AccountManager getInstance() {
    return Holder.instance;
  }
  private AccountManager() {
    // Other instance
  }
  ...
}

每次像這樣寫一遍真是挺煩人的星掰,如果內(nèi)部再有一些依賴的話,會(huì)變得更加撲朔迷離嫩舟。

public class App extend Application {
  private AccountManager mAccount;
  private NetworkManager mNet;
  private DatabaseManager mDb;
  ...
  @Override
  public void onCreate() {
    super.onCreate();
    mAccount = AccountManager.getInstance();
    mNet= NetworkManager.getInstance(this, mAccount);
    mDb= DatabaseManager.getInstance(this, "app.db", 1);
  }
  ...
}

這只是簡(jiǎn)單的例子氢烘,遇到大型項(xiàng)目的時(shí)候,需要初始化的全局實(shí)例更多家厌,并且顯得更加復(fù)雜播玖。

那么為了解決依賴相關(guān)的問(wèn)題,減少每個(gè)項(xiàng)目開啟時(shí)重復(fù)的樣板代碼構(gòu)建饭于,Dagger2就派上了用場(chǎng)蜀踏。

有一個(gè)用來(lái)解釋【依賴注入是什么】的最佳案例就是:ButterKnife

它是這樣用的:

public class MainActivity extends AppCompatActivity {
  @BindView(R.id.button1) View button1;
  @BindView(R.id.text1) View text1;

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

聲明依賴的組件维蒙,通過(guò)注解確定id,使用bind方法傳入this脓斩,完成依賴注入木西。

這就好像你告訴一個(gè)服務(wù)商,你需要什么随静,然后給出收貨地址八千,而服務(wù)商則生產(chǎn)出來(lái),逐一寄到提供的地址燎猛。

Dagger2的用法恋捆,也是上述的形式,唯一例外的是重绷,你將同時(shí)扮演消費(fèi)者和生產(chǎn)商沸停。

這里不考慮復(fù)雜的情況,從入門的角度來(lái)看昭卓,對(duì)于使用Dagger2實(shí)現(xiàn)依賴注入愤钾,很有必要。


二候醒、怎么使用Dagger2能颁?

Dagger2是谷歌forked from square/dagger的一個(gè)分支,谷歌Dagger2開源框架的介紹是:

A fast dependency injector for Android and Java.

對(duì)于其與square的歷史淵源倒淫,github上框架介紹已經(jīng)寫得非常清楚
——翻譯過(guò)來(lái)伙菊,大概是:

  • 消除所有反射,提升運(yùn)行時(shí)性能
  • 編譯時(shí)處理敌土,更快更好的構(gòu)建速度

其實(shí)我是從square官網(wǎng)上發(fā)現(xiàn)的Dagger镜硕,然后在這個(gè)框架的github上看到介紹說(shuō),項(xiàng)目已經(jīng)標(biāo)記為不再維護(hù)返干,推薦使用Dagger2兴枯;而另一方面,網(wǎng)上找到的關(guān)于Dagger的資料矩欠,要么無(wú)法解釋清楚為什么要用念恍,要么就是用起來(lái)特別繁瑣(還不如getInstance簡(jiǎn)單粗暴)。

因此轉(zhuǎn)向了Dagger2晚顷,并且在很長(zhǎng)的一段困惑期中,苦苦掙扎:

要用依賴注入嗎疗疟?真的要用嗎该默?為什么要用呢?用了有什么好處呀策彤?……

后來(lái)干脆自己建立demo栓袖,一步步把玩匣摘,其他資料都不再作為參考,只留下官方sample作為注解的學(xué)習(xí)裹刮。邁出這一步之后音榜,才終于發(fā)現(xiàn)Dagger2的神奇和便利。


1.打開你項(xiàng)目下的gradle文件捧弃,添加Dagger2項(xiàng)目的依賴管理
    // 依賴注入框架
    compile 'com.google.dagger:dagger:2.10'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.10'

PS:這個(gè)版本并非最新版赠叼,有需要的話,可以去官網(wǎng)依賴最新的版本违霞,這里為了穩(wěn)定性嘴办,將不做升級(jí)。

增加依賴注入框架
2.繼承Application創(chuàng)建RandallApp买鸽,然后開始構(gòu)建Dagger2部件和模型
AppComponent接口

使用javax的注解@Singleton標(biāo)記為單例涧郊,即所實(shí)現(xiàn)的類只存在一個(gè)實(shí)例:

import javax.inject.Singleton;

@Singleton
public interface AppComponent {
    // add inject method
}

創(chuàng)建AppModule并依賴Application實(shí)例:

public final class AppModule {
  private final Application application;
  public AppModule(Application application) {
    this.application = application;
  }
  @Provides @Singleton Application provideApplication() {
    return application;
  }
}

創(chuàng)建AndroidModule,因?yàn)槠渌蚣苓€沒有添加依賴眼五,所以這里用Android SDK中的SystemService舉例:

@Module
public final class AndroidModule {
  @Provides @Singleton AudioManager provideAudioManager(Application application) {
    return (AudioManager) application.getSystemService(Context.AUDIO_SERVICE);
  }
  @Provides @Singleton SensorManager provideSensorManager(Application application) {
    return (SensorManager) application.getSystemService(Context.SENSOR_SERVICE);
  }
  @Provides @Singleton Sensor provideSensorAccelerometer(SensorManager sensorManager) {
    return sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
  }
  @Provides @Singleton ConnectivityManager provideConnectivityManager(Application application) {
    return (ConnectivityManager) application.getSystemService(Context.CONNECTIVITY_SERVICE);
  }
}

簡(jiǎn)單說(shuō)明一下AndroidModule和AppModule的關(guān)系:

首先妆艘,AndroidModule是細(xì)節(jié)模型,事實(shí)上全部在AppModule中提供實(shí)例也沒有關(guān)系看幼,但為了明確功能和類型批旺,所以就有了AndroidModule。

可以理解為桌吃,AndroidModule就是AppModule的分身朱沃、子模型,只要通過(guò)這樣的語(yǔ)法就能導(dǎo)入:

@Module(includes = {
  AndroidModule.class,
})
public final class AppModule {
  ...
}

@Module和@Provides都是Dagger2的注解茅诱。前者用于類注解逗物,標(biāo)記這個(gè)類是一個(gè)模型;后者則用于方法注解瑟俭,標(biāo)記返回的實(shí)例可以提供依賴翎卓。

這兩個(gè)注解,可以讓“服務(wù)商”知道自己有哪些物品可以生產(chǎn):

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
  // add inject method
}

AppComponent接口有兩種形式的方法:

  • 當(dāng)需要注入的依賴很多時(shí)摆寄,可以創(chuàng)建inject方法失暴,傳入需要被注入依賴的對(duì)象實(shí)例;
  • 當(dāng)僅需要一個(gè)全局單例時(shí)微饥,可以創(chuàng)建返回對(duì)應(yīng)實(shí)例的方法逗扒。

如何抉擇,當(dāng)由具體需求所決定欠橘。

@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
  // add inject method
  void inject(LoginActivity activity);
  // create return method
  Picasso picasso();
}

完成Dagger2部件與模型的構(gòu)建后矩肩,你的項(xiàng)目結(jié)構(gòu)應(yīng)當(dāng)是這樣:

Dagger2項(xiàng)目結(jié)構(gòu)

隨后,你應(yīng)該make一下工程肃续,使得Dagger2編譯出你所需要的接口實(shí)現(xiàn)類黍檩。

make完成后叉袍,沒有錯(cuò)誤的話,你可以在RandallApp中刽酱,重寫onCreate方法喳逛,然后輸入Dagger...就會(huì)發(fā)現(xiàn)已經(jīng)有了DaggerAppComponent這個(gè)編譯生成的類。

public class RandallApp extends Application {
  @Override public void onCreate() {
    super.onCreate();
    DaggerAppComponent.builder().build();
  }
}

可以看看DaggerAppComponent的一些細(xì)節(jié)棵里,其中可能存在一些困惑:

public final class DaggerAppComponent implements AppComponent {
  private DaggerAppComponent(Builder builder) {
    assert builder != null;
  }

  public static Builder builder() {
    return new Builder();
  }

  public static AppComponent create() {
    return new Builder().build();
  }

  public static final class Builder {
    private Builder() {}

    public AppComponent build() {
      return new DaggerAppComponent(this);
    }

    /**
     * @deprecated This module is declared, but an instance is not used in the component. This
     *     method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
     */
    @Deprecated
    public Builder appModule(AppModule appModule) {
      Preconditions.checkNotNull(appModule);
      return this;
    }

    /**
     * @deprecated This module is declared, but an instance is not used in the component. This
     *     method is a no-op. For more, see https://google.github.io/dagger/unused-modules.
     */
    @Deprecated
    public Builder androidModule(AndroidModule androidModule) {
      Preconditions.checkNotNull(androidModule);
      return this;
    }
  }
}

為什么Module都被“過(guò)時(shí)”了呢润文?因?yàn)楫?dāng)前工程中,沒有任何地方發(fā)出依賴需求衍慎。

提供全局的部件實(shí)例
public class RandallApp extends Application {
  private static AppComponent appcomponent;
  @Override public void onCreate() {
    super.onCreate();
    appcomponent = DaggerAppComponent.builder().build();
  }  
  public static AppComponent appComponent() {
    return appcomponent;
  }
}
使用部件實(shí)例注入實(shí)例
public class LoginActivity extends AppCompatActivity implements LoaderCallbacks<Cursor> {
  ...
  // dependency injection
  @Inject ConnectivityManager cm;
  @Inject AudioManager am;

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

    RandallApp.appComponent().inject(this);
    ...
  }
}

再make一下转唉,此時(shí)所有依賴已經(jīng)成功注入

public final class DaggerAppComponent implements AppComponent {
  private Provider<Application> provideApplicationProvider;

  private Provider<ConnectivityManager> provideConnectivityManagerProvider;

  private Provider<AudioManager> provideAudioManagerProvider;

  private MembersInjector<LoginActivity> loginActivityMembersInjector;

  private DaggerAppComponent(Builder builder) {
    assert builder != null;
    initialize(builder);
  }

  public static Builder builder() {
    return new Builder();
  }

  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {

    this.provideApplicationProvider =
        DoubleCheck.provider(AppModule_ProvideApplicationFactory.create(builder.appModule));

    this.provideConnectivityManagerProvider =
        DoubleCheck.provider(
            AndroidModule_ProvideConnectivityManagerFactory.create(
                builder.androidModule, provideApplicationProvider));

    this.provideAudioManagerProvider =
        DoubleCheck.provider(
            AndroidModule_ProvideAudioManagerFactory.create(
                builder.androidModule, provideApplicationProvider));

    this.loginActivityMembersInjector =
        LoginActivity_MembersInjector.create(
            provideConnectivityManagerProvider, provideAudioManagerProvider);
  }

  @Override
  public void inject(LoginActivity loginActivity) {
    loginActivityMembersInjector.injectMembers(loginActivity);
  }

  public static final class Builder {
    private AppModule appModule;

    private AndroidModule androidModule;

    private Builder() {}

    public AppComponent build() {
      if (appModule == null) {
        throw new IllegalStateException(AppModule.class.getCanonicalName() + " must be set");
      }
      if (androidModule == null) {
        this.androidModule = new AndroidModule();
      }
      return new DaggerAppComponent(this);
    }

    public Builder appModule(AppModule appModule) {
      this.appModule = Preconditions.checkNotNull(appModule);
      return this;
    }

    public Builder androidModule(AndroidModule androidModule) {
      this.androidModule = Preconditions.checkNotNull(androidModule);
      return this;
    }
  }
}

看起來(lái)似乎有一個(gè)問(wèn)題,當(dāng)AppModule是null的時(shí)候稳捆,會(huì)拋出一個(gè)異常赠法。原因在于,AppModule是需要Application實(shí)例去創(chuàng)建乔夯,但是Application是Android在應(yīng)用打開時(shí)才被創(chuàng)建砖织,因此需要在Applicaion的onCreate方法中,構(gòu)建AppComponent時(shí)加入一個(gè)AppModule實(shí)例末荐。

public class RandallApp extends Application {
  private static AppComponent appcomponent;
  @Override public void onCreate() {
    super.onCreate();
    appcomponent = DaggerAppComponent.builder()
            .appModule(new AppModule(this))
            .build();
  }
  public static AppComponent appComponent() {
    return appcomponent;
  }
}

AndroidModule已經(jīng)自動(dòng)new出來(lái)實(shí)例侧纯,無(wú)需多費(fèi)功夫。

三甲脏、總結(jié)

這樣就完成了整個(gè)Dagger2的構(gòu)建工作眶熬,以后再有其他框架的類實(shí)例需要被依賴,只要建立對(duì)應(yīng)的Module類块请,使用provides標(biāo)記的方法提供對(duì)應(yīng)的類實(shí)例娜氏,并包括在AppModule中,然后通過(guò)AppComponent添加inject方法注入需求類實(shí)例即可墩新。

再說(shuō)一點(diǎn)贸弥,維護(hù)期間,如果想改變框架海渊,或者刪除框架绵疲,只需要在AppModule中注釋導(dǎo)入的對(duì)應(yīng)Module,然后在需求類實(shí)例中臣疑,將Inject的依賴注釋即可盔憨。

Dagger2的基本使用就到這里,后面開始建立基于DataBinding框架的MVVM設(shè)計(jì)模式讯沈。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末郁岩,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌驯用,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件儒老,死亡現(xiàn)場(chǎng)離奇詭異蝴乔,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)驮樊,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門薇正,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人囚衔,你說(shuō)我怎么就攤上這事挖腰。” “怎么了练湿?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵猴仑,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我肥哎,道長(zhǎng)辽俗,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任篡诽,我火速辦了婚禮崖飘,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘杈女。我一直安慰自己朱浴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布达椰。 她就那樣靜靜地躺著翰蠢,像睡著了一般。 火紅的嫁衣襯著肌膚如雪砰碴。 梳的紋絲不亂的頭發(fā)上躏筏,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音呈枉,去河邊找鬼趁尼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛猖辫,可吹牛的內(nèi)容都是我干的酥泞。 我是一名探鬼主播,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼啃憎,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼芝囤!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤悯姊,失蹤者是張志新(化名)和其女友劉穎羡藐,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悯许,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡仆嗦,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了先壕。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘩扼。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖垃僚,靈堂內(nèi)的尸體忽然破棺而出集绰,到底是詐尸還是另有隱情,我是刑警寧澤谆棺,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布栽燕,位于F島的核電站,受9級(jí)特大地震影響包券,放射性物質(zhì)發(fā)生泄漏纫谅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一溅固、第九天 我趴在偏房一處隱蔽的房頂上張望付秕。 院中可真熱鬧,春花似錦侍郭、人聲如沸询吴。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)猛计。三九已至,卻和暖如春爆捞,著一層夾襖步出監(jiān)牢的瞬間奉瘤,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工煮甥, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留盗温,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓成肘,卻偏偏與公主長(zhǎng)得像卖局,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子双霍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,102評(píng)論 25 707
  • 轉(zhuǎn)載請(qǐng)標(biāo)明原文地址:http://www.reibang.com/p/dc163215bc7e 本來(lái)打算繼續(xù)寫 M...
    junerver閱讀 2,773評(píng)論 12 76
  • 本文的分析基于dagger2的2.7版本砚偶。 谷歌開發(fā)維護(hù)的Dagger2出來(lái)有很長(zhǎng)時(shí)間了批销,目前在很多開源項(xiàng)目上也能...
    sososeen09閱讀 13,602評(píng)論 31 108
  • 總算是接近酷暑的尾聲了,剛立秋的天氣的涼意滲透到熱血中染坯,恰到好處的消暑劑均芽,只怕是貪婪的人會(huì)上癮。 我這脆弱的肚子也...
    愛笑的迷妹閱讀 212評(píng)論 0 0
  • 轉(zhuǎn)自好友 周兵 她已嫁為人婦单鹿,有個(gè)可愛的女兒骡技,叫我舅舅。距離畢業(yè)后唯一的一次見面已經(jīng)有三年半羞反。我們偶爾聊天,每次都...
    云木西閱讀 338評(píng)論 0 0