Android 神兵利器Dagger2使用詳解(三)MVP架構(gòu)下的使用

前言

本系列所有文章:

Android 神兵利器Dagger2使用詳解(一)基礎(chǔ)使用
Android 神兵利器Dagger2使用詳解(二)Module&Component源碼分析
Android 神兵利器Dagger2使用詳解(三)MVP架構(gòu)下的使用
Android 神兵利器Dagger2使用詳解(四)Scope注解的使用及源碼分析
告別Dagger2模板代碼:DaggerAndroid使用詳解
告別Dagger2模板代碼:DaggerAndroid原理解析
該系列首發(fā)于我的CSDN專欄 :
Android開發(fā):Dagger2詳解

在我的上一篇文章中,我們通過一點(diǎn)點(diǎn)分析@Module小染、@Inject以及@Component注解生成的源碼翘瓮,了解了Dagger2依賴注入魔法的根源:

1、 @Inject 注解構(gòu)造 生成“大眾”工廠類 或者 @Module +@Providers 提供注入“私有”工廠類

2裤翩、通過Component 創(chuàng)建獲得Activity,獲得工廠類Provider资盅,統(tǒng)一交給Injector

3、Injector將Provider的get()方法提供的對(duì)象踊赠,注入到Activity容器對(duì)應(yīng)的成員變量中

我們就可以直接使用容器(Activity)中對(duì)應(yīng)的成員變量了

借用一張圖更形象一些:(圖片來源地址)感謝@牛曉偉

這里寫圖片描述

但是上文的使用方法實(shí)在有些簡(jiǎn)陋呵扛,不足以體現(xiàn)Dagger2為什么好用,或者說好用在哪臼疫,本文將會(huì)以正常實(shí)際開發(fā)為例择份,闡述Dagger2實(shí)際開發(fā)中的使用方式。

一烫堤、全局類 AppComponent & AppModule

我們先簡(jiǎn)單將我們開發(fā)時(shí)需要依賴注入的對(duì)象進(jìn)行簡(jiǎn)單的分類:

1荣赶、全局變量
這類變量一般為單例模式存在,比如SharedPerferences對(duì)象鸽斟、Application對(duì)象拔创,還有ServiceManager(網(wǎng)絡(luò)請(qǐng)求API管理類)等等,這些東西也許我們只想初始化一次富蓄,之后處處使用就好了剩燥。
2、局部變量
這些變量可能我們只想在某個(gè)地方使用立倍,比如Student對(duì)象灭红,我們一般也不會(huì)只聲明一次。

開發(fā)中口注,局部變量尚還好說变擒,我們?cè)跊]有Dagger的時(shí)候,用的時(shí)候new一個(gè)就可以了寝志,類似于SharedPerferences這種對(duì)象我們就比較頭疼娇斑,也沒有必要每次都去context.getSharedPerferences()吧,但是我們有了Dagger材部,我們可以通過依賴注入的方式毫缆,僅僅初始化一次,之后每次想使用乐导,只需要@Inject注解苦丁,就能直接使用了,是不是很方便呀兽叮。

說干就干芬骄,我們創(chuàng)建一個(gè)全局的AppComponent 和 AppModule:

0猾愿、首先創(chuàng)建一個(gè)自定義的Application類:

public class MyApplication extends Application{
    @Override
    public void onCreate() {
        super.onCreate();
    }
}

記得在清單文件中使用這個(gè)類哦 ~

1、創(chuàng)建AppModule账阻,提供你想要提供的全局變量

@Module
public class AppModule {

    private MyApplication application;

    public AppModule(MyApplication application) {
        this.application = application;
    }
    
    //提供全局的sp對(duì)象
    @Provides
    SharedPreferences provideSharedPreferences(){
        return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
    }
    
    //提供全局的Application對(duì)象
    @Provides
    MyApplication provideApplication(){
        return application;
    }
    
    //你還可以提供更多.......
}

2蒂秘、創(chuàng)建AppComponent,然后ctrl+F9編譯

@Component(modules = AppModule.class)
public interface AppComponent {

    SharedPreferences sharedPreferences();

    MyApplication myApplication();
}

3淘太、編譯成功后姻僧,修改Application類,把Component注入器注入你的Application中:

public class MyApplication extends Application{

    @Override
    public void onCreate() {
        super.onCreate();
        inject();
    }

    private void inject() {
        AppComponent appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(this))
                .build();
        ComponentHolder.setAppComponent(appComponent);
    }
}

ComponentHolder類蒲牧,方便獲取appComponent對(duì)象:

public class ComponentHolder {
    private static AppComponent myAppComponent;

    public static void setAppComponent(AppComponent component) {
        myAppComponent = component;
    }

    public static AppComponent getAppComponent() {
        return myAppComponent;
    }
}

ok撇贺,這樣,無論何處冰抢,我們需要獲得AppComponent對(duì)象時(shí)松嘶,只需要調(diào)用ComponentHolder.getAppComponent()就可以啦!

注意:

我們需要知道剛才這些文件的作用:

1挎扰、AppModule:初始化全局變量
2翠订、AppComponent:注入器,儲(chǔ)存了我們要用到的全局變量對(duì)象
3遵倦、MyApplication: 在onCreate()方法中唯一一次初始化了AppComponent對(duì)象尽超,并放入了ComponentHolder中。

MyApplication (作為參數(shù)初始化)-> AppModule(初始化全局變量) -> (注入) AppComponent ->(存儲(chǔ)到)ComponentHolder

好的梧躺,現(xiàn)在我們已經(jīng)把所有要用到的全局變量存到了AppComponent中似谁,接下來我們創(chuàng)建一個(gè)普通的容器Activity。

二掠哥、日常開發(fā) Activity相關(guān)

0巩踏、我們先創(chuàng)建一個(gè)普通的Module和Component

@Module
public class A02Module {

    private A02Activity activity;

    public A02Module(A02Activity activity) {
        this.activity = activity;
    }
    
    //顯然我們并不是很多地方都需要某對(duì)象,我們?cè)谛枰迷搶?duì)象的界面的Module中提供注入即可
    @Provides
    Student provideStudent() {
        return new Student();
    }

}
@Component(modules = A02Module.class, dependencies = AppComponent.class)
public interface A02Component {

    void inject(A02Activity activity);

}

請(qǐng)注意Component接口续搀,我們發(fā)現(xiàn)多了這樣一行代碼:

dependencies = AppComponent.class

其實(shí)也很好理解蛀缝,比如說我們需要Student對(duì)象和SharedPerferences對(duì)象,前者是A02Component提供的目代,后者則需要「依賴(dependencies)」AppComponent提供,等于這次inject(A02Activity activity)依賴注入需要兩個(gè)Component共同出力完成嗤练。

1榛了、編譯成功后,在Activity中聲明:

public class A02Activity extends AppCompatActivity {

    @Inject
    Student student;
    @Inject
    SharedPreferences sp;

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

    private void inject() {
        DaggerA02Component.builder()
                .appComponent(ComponentHolder.getAppComponent())//添加appComponent注入器
                .a02Module(new A02Module(this))
                .build()
                .inject(this);

        Log.i("tag", "注入完畢煞抬,Student = " + student.toString());
        Log.i("tag", "注入完畢霜大,sp = " + sp.toString());
    }
}

查看一下結(jié)果,依賴注入成功:

這里寫圖片描述

是不是很神奇革答!試想一下战坤,如果我們有很多類似的對(duì)象需要經(jīng)常使用曙强,我們?cè)僖膊恍枰看味既コ跏蓟苯覢Inject途茫,然后直接使用就可以了;

更方便的是碟嘴,如果我們需要修改(比如SharedPreferences文件的名字等等),只需要跑到提供它的Module文件中:

@Module
public class AppModule {

    ...
    ...
    ...
    
    //提供全局的sp對(duì)象 -> 修改
    @Provides
    SharedPreferences provideSharedPreferences(){
     //   return application.getSharedPreferences("spfile", Context.MODE_PRIVATE);
        return application.getSharedPreferences("New spfile", Context.MODE_PRIVATE);
    }

    ...
    ...
    ...
}

當(dāng)然囊卜,你的Activity容器中代碼也能變得更加賞心悅目:

這里寫圖片描述

三*娜扇、源碼分析:

因?yàn)樵谖业纳弦黄恼?a target="_blank" rel="nofollow">Android 神兵利器Dagger2使用詳解(二)Module&Component源碼分析 中已經(jīng)對(duì)原理有了基本的了解,所以我們可以輕松看懂實(shí)現(xiàn)原理栅组。

簡(jiǎn)單分析下源碼雀瓢,如下為編譯器自動(dòng)生成的代碼:

這里寫圖片描述

1、App全局類源碼(global包下的三個(gè)類)

提供對(duì)象的工廠類:
AppModule_ProvideApplicationFactory.java
AppModule_ProvideSharedPreferencesFactory.java
注入器:
DaggerAppComponent.java

工廠類直接略過玉掸,只不過是通過傳入Module對(duì)象初始化刃麸,然后調(diào)用get()方法獲得Module中對(duì)應(yīng)的對(duì)象罷了(若不清楚請(qǐng)參考我的上一篇文章,分析得比較清楚)司浪。

注入器的改變:

public final class DaggerAppComponent implements AppComponent {

  private Provider<SharedPreferences> provideSharedPreferencesProvider;

  private Provider<MyApplication> provideApplicationProvider;

  private DaggerAppComponent(Builder builder) {
    ...
    initialize(builder);
  }

    ...
  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
    //2.初始化2個(gè)工廠類
    this.provideSharedPreferencesProvider =
        AppModule_ProvideSharedPreferencesFactory.create(builder.appModule);

    this.provideApplicationProvider = AppModule_ProvideApplicationFactory.create(builder.appModule);
  }
    
    //3.想要sp對(duì)象嗎泊业?appComponent調(diào)用一下這個(gè)方法就有了
  @Override
  public SharedPreferences sharedPreferences() {
    return provideSharedPreferencesProvider.get();
  }

  @Override
  public MyApplication myApplication() {
    return provideApplicationProvider.get();
  }

  public static final class Builder {
      //1、參數(shù)傳入断傲,初始化appModule
      private AppModule appModule;
       ...
       ...
  }
}

還是老三樣脱吱,三板斧,沒什么難度认罩。

2箱蝠、A02Component源碼分析:

提供Student對(duì)象的工廠類:
A02Module_ProvideStudentFactory.java
把對(duì)象注入到容器的Injcetor類:
A02Activity_MembersInjector.java
注入器:
DaggerA02Component.java

1、工廠略過不提垦垂,提供Student對(duì)象的類而已宦搬;
2、Injector 我們上一篇文章中可以知道劫拗,通過Component把需要對(duì)象的容器(activity)间校、以及對(duì)象工廠(studentProvider)給Injector后,Injector把對(duì)象賦值給容器中對(duì)應(yīng)的屬性而已页慷,比如:

activity.student = studentProvider.get();

3憔足、DaggerA02Component,執(zhí)行順序參考代碼中注釋:

public final class DaggerA02Component implements A02Component {
  private Provider<Student> provideStudentProvider;

  private Provider<SharedPreferences> sharedPreferencesProvider;

  private MembersInjector<A02Activity> a02ActivityMembersInjector;

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

  public static Builder builder() {
    return new Builder();
  }
    
  @SuppressWarnings("unchecked")
  private void initialize(final Builder builder) {
  
    //3.初始化所有對(duì)象的工廠Provider
    //3.1 從builder中的Module中獲得學(xué)生Student工廠
    this.provideStudentProvider = A02Module_ProvideStudentFactory.create(builder.a02Module);
    
     //3.2 從builder中的appComponent中獲得SP工廠
    this.sharedPreferencesProvider =
        new Factory<SharedPreferences>() {
          private final AppComponent appComponent = builder.appComponent;

          @Override
          public SharedPreferences get() {
            return Preconditions.checkNotNull(
                appComponent.sharedPreferences(),
                "Cannot return null from a non-@Nullable component method");
          }
        };
    //4酒繁、把所有工廠都傳到Injector中
    this.a02ActivityMembersInjector =
        A02Activity_MembersInjector.create(provideStudentProvider, sharedPreferencesProvider);
  }

  @Override
  public void inject(A02Activity activity) {
     //5滓彰、把a(bǔ)ctivity放入到Injector中(接下來結(jié)果很明顯了...)
    a02ActivityMembersInjector.injectMembers(activity);
  }

  public static final class Builder {
    private A02Module a02Module;

    private AppComponent appComponent;

    private Builder() {}

    public A02Component build() {
      if (a02Module == null) {
        throw new IllegalStateException(A02Module.class.getCanonicalName() + " must be set");
      }
      if (appComponent == null) {
        throw new IllegalStateException(AppComponent.class.getCanonicalName() + " must be set");
      }
      return new DaggerA02Component(this);
    }
    //2.傳入a02Module
    public Builder a02Module(A02Module a02Module) {
      this.a02Module = Preconditions.checkNotNull(a02Module);
      return this;
    }
    //1.傳入appComponent
    public Builder appComponent(AppComponent appComponent) {
      this.appComponent = Preconditions.checkNotNull(appComponent);
      return this;
    }
  }
}

代碼雖然變多了,但是州袒,還是很好理解...

小結(jié)

不知道大家有沒有感到Dagger2的好用揭绑,確實(shí)是開發(fā)時(shí)解耦神器啊郎哭!

膜拜他匪!

如果有地方看不懂菇存,建議自己嘗試敲兩遍,然后參考編譯器生成的代碼邦蜜,多想想依鸥,可能一開始會(huì)有點(diǎn)繞,但還是可以看懂的畦徘。

代碼已托管Github毕籽,源碼傳送門,點(diǎn)我查看

在我的下一篇 Android 神兵利器Dagger2使用詳解(四)Scope注解的使用及源碼分析 中井辆,將會(huì)結(jié)合源碼关筒,學(xué)習(xí)并加深@Scope注解

2017/8/24日更新

事實(shí)上杯缺,Android開發(fā)中使用Dagger,開發(fā)人員仍然需要面對(duì)一些問題蒸播。

google工程師們嘗試彌補(bǔ)Dagger的問題,于是Dagger2-Android,基于Dagger2萍肆,應(yīng)用于Android開發(fā)袍榆,由google開發(fā)的的拓展庫應(yīng)運(yùn)而生:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市塘揣,隨后出現(xiàn)的幾起案子包雀,更是在濱河造成了極大的恐慌,老刑警劉巖亲铡,帶你破解...
    沈念sama閱讀 206,482評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件才写,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡奖蔓,警方通過查閱死者的電腦和手機(jī)赞草,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來吆鹤,“玉大人厨疙,你說我怎么就攤上這事∫晌瘢” “怎么了沾凄?”我有些...
    開封第一講書人閱讀 152,762評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)知允。 經(jīng)常有香客問我搭独,道長(zhǎng),這世上最難降的妖魔是什么廊镜? 我笑而不...
    開封第一講書人閱讀 55,273評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮唉俗,結(jié)果婚禮上嗤朴,老公的妹妹穿的比我還像新娘配椭。我一直安慰自己,他們只是感情好雹姊,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,289評(píng)論 5 373
  • 文/花漫 我一把揭開白布股缸。 她就那樣靜靜地躺著,像睡著了一般吱雏。 火紅的嫁衣襯著肌膚如雪敦姻。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評(píng)論 1 285
  • 那天歧杏,我揣著相機(jī)與錄音镰惦,去河邊找鬼。 笑死犬绒,一個(gè)胖子當(dāng)著我的面吹牛旺入,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播凯力,決...
    沈念sama閱讀 38,351評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼茵瘾,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了咐鹤?” 一聲冷哼從身側(cè)響起拗秘,我...
    開封第一講書人閱讀 36,988評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎祈惶,沒想到半個(gè)月后雕旨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡行瑞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,948評(píng)論 2 324
  • 正文 我和宋清朗相戀三年奸腺,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片血久。...
    茶點(diǎn)故事閱讀 38,064評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡突照,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出氧吐,到底是詐尸還是另有隱情讹蘑,我是刑警寧澤,帶...
    沈念sama閱讀 33,712評(píng)論 4 323
  • 正文 年R本政府宣布筑舅,位于F島的核電站座慰,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏翠拣。R本人自食惡果不足惜版仔,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,261評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蛮粮,春花似錦益缎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至变泄,卻和暖如春令哟,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背妨蛹。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評(píng)論 1 262
  • 我被黑心中介騙來泰國打工屏富, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滑燃。 一個(gè)月前我還...
    沈念sama閱讀 45,511評(píng)論 2 354
  • 正文 我出身青樓役听,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親表窘。 傳聞我的和親對(duì)象是個(gè)殘疾皇子典予,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,802評(píng)論 2 345

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