Android:Dagger2系列2 實例解析(更新完畢)

上一篇:Android:Dagger2系列1 初識
下一篇:Android:Dagger2系列3 深入探究(更新ing)

這篇文章會分享一下實際應用中的Dagger2如何使用,以及Dagger2通過apt插件如何給我們生成代碼,以及生成的代碼之間的關聯。

下面說一下模擬的業務場景:

主界面MainActivity通過MainPresenter去請求一個接口,并返回數據。這里用的是MVP+Retrofit2+RxJava,如果不熟悉可以先不管,因為不會涉及太多,而這篇內容主要分享的是Dagger2。
如果mvp不清楚的可以點擊 mvp google 寫法;RxJava和Retrofit后期我也會分享出來(知道的略過),歡迎關注?。。?/p>

先看下關于Dagger部分的包目錄結構:

google官方demo 是按照業務來分包的,個人比較喜歡按照組件來分。

目錄結構.png

首先我需要一個全局的網絡請求對象IRetrofitRequest放在Application,并且是單例的。所以寫了一個RetrofitModule提供IRetrofitRequest實例。

代碼塊1:

@Singleton
@Component(modules = {AppModule.class, RetrofitModule.class})
public interface AppComponent {
    IRetrofitRequest request();
    Context getContext();
}

其中request();方法返回的IRetrofitRequest對象需要上面代碼塊1:依賴的RetrofitModule類中進行實例化:如下代碼

代碼塊2:

@Module
public class RetrofitModule {
    @Provides
    @Singleton
    public IRetrofitRequest getService() {
        OkHttpClient httpClient = new OkHttpClient.Builder()
                .connectTimeout(30, TimeUnit.SECONDS)//設置請求超時時間
                .retryOnConnectionFailure(true)//設置出現錯誤進行重新連接
                .build();
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(UrlConst.URL)
                .addConverterFactory(GsonConverterFactory.create())
                .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
                .client(httpClient)
                .build();
        return retrofit.create(IRetrofitRequest.class);
    }
}

代碼塊2:中提供的IRetrofitRequest 實例對象必須要用@Provides標注,該對象是單例的所以用@Singleton標注,這里為什么用這兩個注解標注之后就能實現為AppComponent提供單例的實例,稍后會進行Dagger2生成的代碼解析。
當然一個Component類可以依賴多個Module,如代碼塊1:中還依賴了AppModule,AppModule中提供了在Component方法名是getContext()的實例對象,如下代碼:

代碼塊3:

@Module
public class AppModule {
    private Context context;
    public AppModule(Context context) {
        this.context = context;
    }
    @Provides
    public Context getContext() {
        return context;
    }
}

如代碼塊2和3所示,所有的被Component依賴的Module都必須用@Module注解標注。因為Dagger2需要這些標注通過apt插件自動生成代碼。
在AppComponent中提供的IRetrofitRequest單例對象如何在Application中使用呢?

代碼塊4:

public class App extends Application {
    private static AppComponent appComponent;
    @Override
    public void onCreate() {
        super.onCreate();
        appComponent = DaggerAppComponent.builder()
                .appModule(new AppModule(getApplicationContext()))
                .retrofitModule(new RetrofitModule())
                .build();
    }
    public static AppComponent getComponent() {
        return appComponent;
    }
}

代碼塊4:中的DaggerAppComponent是Dagger2幫我們自動生成的,只要編譯一下就可以自動生成:


編譯.png

AppComponent需要初始化依賴的兩個Module(AppModule和RetrofitModule),這里生成的DaggerAppComponent是通過構建者模式進行初始化的。

.appModule(new AppModule(getApplicationContext()))
.retrofitModule(new RetrofitModule())

最后創建的AppComponent就提供了IRetrofitRequest全局單例對象,整個app的網絡訪問都可以通過該對象進行調用。

AppComponent后期拓展:

一個全局的變量現在統一都可以放在AppComponent中進行管理,這個demo中有網絡請求的一個單例接口對象,一個是全局的Context對象。后期肯定會有其他的都可以放在AppModule中進行實例化,或者單獨再寫一個Module依賴到AppComponent中。

在MainActivity中如何進行使用:

這里用的是mvp開發模式,所以需要一個Presenter:MainActivityPresenter,需要傳遞一個參數,用于操作MainActivity界面:MainActivityContract.View,而這個MainActivityPresenter誰來提供呢?當然是Component通過依賴的Module來提供,看看MainActivity的Component和Module。

代碼塊5:

@ActivityScope
@Component(dependencies = AppComponent.class, modules = {MainActivityModule.class})
public interface MainActivityComponent {
    void inject(MainActivity mainActivity);
}
代碼塊6:

@Module
public class MainActivityModule {
    private MainActivityContract.View view;
    public MainActivityModule(MainActivityContract.View view) {
        this.view = view;
    }
    @ActivityScope
    @Provides
    public MainActivityPresenter getMainActivityPresenter() {
        return new MainActivityPresenter(view);
    }
}
這里的代碼塊5和6,你會發現我們在MainActivityModule 里提供了MainActivityPresenter實例,但是在MainActivityComponent接口里并沒有寫上提供MainActivityPresenter的方法,另外還多了一個void inject(MainActivity mainactivity),這里跟AppComponent中的(代碼塊1)有區別是咋回事?等會解釋。

再看MainActivity代碼:

代碼塊6:

public class MainActivity extends BaseActivity implements MainActivityContract.View {
    @Inject
    MainActivityPresenter presenter;
    @Inject
    SecondActivityPresenter secondActivityPresenter;
    @Bind(R.id.textView)
    TextView textView;
    @Bind(R.id.textView2)
    TextView textView2;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        presenter.start();
        secondActivityPresenter.set();
    }
    @Override
    public void providers() {//該方法是BaseActivity中onCreate()中調用的抽象方法
        DaggerMainActivityComponent.builder()
                .mainActivityModule(new MainActivityModule(this))
                .appComponent(App.getComponent())
                .build()
                .inject(this);
    }
    @OnClick({R.id.textView, R.id.textView2})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.textView:
                startActivity(new Intent(this, SecondActivity.class));
                break;
            case R.id.textView2:
                break;
            default:
                break;
        }
    }
    @Override
    public void showSuccess() {
        T.show(this, "成功");
    }
    @Override
    public void showFailed() {
        T.show(this, "失敗");
    }
}
先解釋一下providers()方法:該方法是BaseActivity中onCreate()中調用的抽象方法。

注意看代碼中的

@Inject
MainActivityPresenter presenter;

注意1:但是在整個MainActivity中卻找不到初始化的過程,再看providers()方法中的代碼,跟App中有區別的是,我這里并沒有寫成

  MainActivityComponent mainActivityComponent = DaggerMainActivityComponent.builder()
                .mainActivityModule(new MainActivityModule(this))
                .appComponent(App.getComponent())
                .build();

注意2:還有的區別是多了一個

.inject(this);

原因:

1:因為在App中我并沒有哪個對象的聲明用了@Inject注解進行標注,而且App中的AppComponent實例對象需要給其他Activity或者類使用。
2:在該demo中的MainActivity,我們不需要其他地方用到MainActivityComponent對象,我們只是在MainActivity用到MainPresenter對象,我們可以不用通過MainActivityComponent中的某一個方法獲得MainPresenter對象,我們在用@Inject標注MainActivityPresenter presenter的時候需要把在哪里聲明的外部類(這里是MainActivity)注入到MainComponent中,就是上面(代碼塊5)說過的沒有提供返回MainPresenter的方法卻多了一個void inject(MainActivity mainactivity);這里的返回值是void的inject方法名可以是任意的,但是最好寫成inject(官方寫法)。

最后只要調用了providers()方法,我們的MainPresenter presenter對象就已經被初始化了,這個時候就可以通過presenter.start()去調用網絡接口請求數據了;

只要我們配置了以上的Component,Module,編譯之后Dagger2就會通過apt插件生成一系列代碼。

那么一系列代碼到底是怎樣的?到底是怎樣工作的呢?

先看下生成的代碼目錄結構:

Dagger2通過apt生成的代碼目錄結構.png

可以看到生成的代碼包名還是跟自己代碼中的一樣,生成的代碼的類名也有一定的規則。

看看這些代碼是根據什么注解生成的:

  • 用@Component注解標注的xxxComponent類會生成DaggerxxxComponent類
  • 用@Module注解標注的xxxModule中用@Provides注解標注的每個方法都會生成一個類,這個類是一個工廠模式,提供對象實例,比如:
@Module
public class AAModule{
    @ActivityScope
    @Providespublic 
    BB getBB() {
        return new BB();
    }
}

getBB()方法就會生成AAModule_GetBBFactory類。

  • 如果一個類的構造函數用了@Inject注解標注:例如:
public class CC{
    @Inject
    public CC() {
    }
}

就會生成CC_Factory類。

  • 如果一個類中有用@Inject注解標注對象聲明他就會生成:比如demo中的MainActivity;
public class MainActivity extends BaseActivity implements MainActivityContract.View {   
     @Inject
     MainActivityPresenter presenter;
}

Dagger2就會自動生成MainActivity_MembersInjector。

看看這些代碼之間的關聯

Dagger2 自動生成的代碼關聯分析圖.jpg

分析圖箭頭的結尾是DaggerMainActivityComponent,也就是最后暴露給我們的就是DaggerMainActivityComponent這個類,其它幫助我們生成的代碼都可以不用關心。

分析圖或許會有疏漏和不對,基本情況應該就是這樣,更多細節性的代碼可以查看項目gitlab: demo

上一篇:Android:Dagger2系列1 初識
下一篇:Android:Dagger2系列3 深入探究(更新ing)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容

  • 部分內容參考自:[Android]使用Dagger 2依賴注入 - DI介紹(翻譯)[Android]使用Dagg...
    AItsuki閱讀 47,719評論 66 356
  • 寫在前面:我目前就職于阿里巴巴-菜鳥,團隊目前缺人,招聘java和客戶端開發,招聘對象為:社招和19屆畢業的校招生...
    littleKang閱讀 115,424評論 93 745
  • 不知道你們的身邊有沒有這么一種人,莫名其妙的他就給你吊臉子,好像突然之間你欠了他五百萬。你剛畢業,他總是嘲笑你...
    伊筱葵閱讀 445評論 0 1
  • (一)樹歸樹 林歸林 也不知道怎么了,這幾天的天氣異常得很。整日里,天都是灰蒙蒙的,好像被一個大悶蓋兒蓋住了似的,...
    落在枝頭閱讀 548評論 0 0
  • 每天1000字,把它分解開來,如果一句話十個字的話平均,也就是,每天說100句話,然后把它寫下來,1000字的任務...
    倔強的挑夫閱讀 379評論 0 0