當 Dagger2 應用在 MVP 框架中
關于 Dagger2 自己一只想搞明白,但是從去年開始到現在,說真的,看過不少介紹 Dagger2 的文章,但自己一只云里霧里,最近打算把 Dagger2 應用到 MVP 框架中去,所以就重新翻看相關技術文章,重新學習下,下面算是自己這兩天學習 Dagger2 后對 Dagger2 的認識,不一定都正確,如果 錯誤,歡迎指正,只要從代碼角度出發,認識 Dagger2。
Note:如果你對 MVP 和 Retrofit 都不熟悉,這篇文章可能不適合你閱讀。
學習初衷
前段時間按照 MVP 架構重構過一個妹子客戶端 GankDaily ,關于 MVP 具體在 GankDaily 中如何使用,自己也專門寫過一篇文章介紹過, MVP 模式在 GankDaily 中的應用 ,如果看過 GankDaily 的代碼,其實不僅僅是 GankDaily 這個項目,所有 使用 MVP 架構的項目,都會存在一個問題,那就是每個 Activity 中一定會有一個 Presenter 實例對象,如下所示
/**
- the presenter of this Activity
*/
private MainActivityPresenter mPresenter;</pre>
它是 Activity 的控制器,所有的數據請求、界面更新都直接或間接的被它控制。然后每個Activity 都需要在 onCreate 中 去實例化它,如下所示
mPresenter = new MainActivityPresenter(this, user);
上面看上去沒什么不妥,但是如果你聽過依賴注入(關于依賴注入,這里一哥們寫的文章介紹的不錯, 依賴注入原理 ),就知道這種使用 new 關鍵字去實例化一個對象有諸多不好的地方, 這種 new 實例的方式 其實是一種硬編碼。為什么?
試想一下,如果以后 MainActivityPresenter 的構造方法中的參數發生變化,那么此時,不僅僅在 MainActivityPresenter 類中改動了 MainActivityPresenter 的構造器相關的代碼,你還需要在所有使用到 MainActivityPresenter 的地方修改代碼,這不就是硬編碼嗎?
那你肯定想問? “你說說除了這樣的方法,還有什么方法能避免你上面說的這種問題呢?”
答案就是今天的主題 – Dagger2
試想一下,如果把 MainActivityPresenter 實例的獲取,事先通過一個輔助類約定好,那么具體使用的時候,通過輔助類 去生成 MainActivityPresenter 的對象,那么是不是會好點,其實這個輔助類,在我理解而來就是 Dagger2 中的 Module 概念。
當然 Dagger2 除了可以解決硬編碼的問題,還有其他很多好處,如方便測試等等。
以上算是自己學習的初衷,總結一下,我使用 Dagger2 的愿景或者最終的預期的結果就是 在Activity 中看不到 new Presenter() 這樣的代碼,直接在 定義 Presenter 實例時,用 @Inject 注解一下,Presenter 的實例就生成。
最終結果
下面自己將會使用 Dagger2 + MVP架構模式 + Retrofit 演示一個簡單的 demo 。
這個demo 只有一個 MainActivity界面,最終的效果是在 MainActivity 通過 MainActivityPresenter 獲取到一個 字符串顯示在 MainActivity 。
這里關于 Dagger2 和 MVP 你應該已經了解他們為什么存在,但是對于 Retrofit 可能有點疑惑,關于 Retrofit:
Retrofit:用來模擬一般的網絡數據獲取,這個目前大家用的比較多,也確實是一個很方便的庫,加上支持 RxJava ,它用于獲取數據真是極方便的。 既然要用 Retrofit ,就應該知道整個應用中,最好只存在一個 Retrofit 實例。
環境配置
根目錄的 build 文件加入 android-apt 支持
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.1.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } }</pre>
在 app 目錄的 build 文件中加入 apt plugin 的支持
apply plugin: 'com.neenbedankt.android-apt'
加入 Dagger2 retrofit OkHttp 依賴
//dagger2 compile 'com.google.dagger:dagger:2.0' apt 'com.google.dagger:dagger-compiler:2.0' provided 'org.glassfish:javax.annotation:10.0-b28'//Square compile 'com.jakewharton:butterknife:6.1.0' compile 'com.squareup.retrofit:retrofit:1.9.0' compile 'com.squareup.okhttp:okhttp:2.3.0'</pre>
在 Application 中實踐 Dagger2
現在依賴都已經加入,開始動手使用 Dagger2 ,第一步考慮我們的 Application。
先創建 AppApplication,并在 AndroidMainfest.xml 注冊好。
public class AppApplication extends Application{
<pre>@Override public void onCreate() { super.onCreate(); }</pre>}
<application android:name=".AppApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" ></application></pre>
這里想想,在 Application 中應該做點什么呢?Application中一般可以做一些全局性相關的事。
對于 Retrofit ,前面也說了應該在全局中只存在一個實例,所以 Application 中可以有一個全局性的 Retrofit。
說到這里,感覺說的有點費勁,主要是自己理解的也還不夠深入,先這樣吧,反正是寫給自己以后看的,如果對你,正在讀文章的你造成了 困惑,還請見諒。如果讀文章有點費勁,建議直接看已經存在于Github上最終的 源代碼
基于上面的分析, Application 可能需要一個依賴,依賴于 ApiServiceModule(提供 Retrofit 實例), 這里我們看看 ApiServiceModule 的代碼實現
@Module public class ApiServiceModule { private static final String ENDPOINT="";@Provides @Singleton OkHttpClient provideOkHttpClient() { OkHttpClient okHttpClient = new OkHttpClient(); okHttpClient.setConnectTimeout(60 * 1000, TimeUnit.MILLISECONDS); okHttpClient.setReadTimeout(60 * 1000, TimeUnit.MILLISECONDS); return okHttpClient; } @Provides @Singleton RestAdapter provideRestAdapter(Application application, OkHttpClient okHttpClient) { RestAdapter.Builder builder = new RestAdapter.Builder(); builder.setClient(new OkClient(okHttpClient)) .setEndpoint(ENDPOINT); return builder.build(); } @Provides @Singleton ApiService provideApiService(RestAdapter restAdapter) { return restAdapter.create(ApiService.class); }
}</pre>
任何 Module 類的開頭都以 @Module 進行注解。
然后,最終 ApiServiceModule 需要提供給 AppApplication ,這里不能直接將 ApiServiceModule 給 AppApplication , 現在需要一個組件對他進行整合 這個組件我們對他命名為 AppComponent下面是它的實現。
@Singleton // 如果這里有多個 可以用逗號隔開,繼續追加 @Component(modules = {ApiServiceModule.class})public interface AppComponent {
ApiService getService();
}</pre>
這里你會看到他是一個接口,然后在類申明的開始的位置,用 @Component 指定了他具體的依賴的類。
這個接口只有一個方法,返回了之前設想的一個全局實例 ApiService。
既然是接口,那么一定有類去實現他,設想一下,如果有一個類實現了上面的接口, 我們只要在 AppApplication 中得到 這個實現的對象,那這個對象不就是 AppComponent,然后我們不就 通過 AppComponent 獲取到實例對象了嗎?恩~ 就應該是這樣。
現在定義好 AppComponent 接口,然后經過編譯,當你在編輯器 打出 Dagger 編輯器就會自動提示 DaggerAppComponent 這個類。
![]()
恩?這是怎么回事?
這其實是 Dagger2 幫你自動生成的。
現在在 AppApplication 中直接使用 DaggerAppComponent,用它來得到 AppComponent 對象,然后有了 AppComponent 那么 ApiService 不就隨之而來了嗎?
AppApplication 中代碼現在如下所示
public class AppApplication extends Application{public static AppApplication get(Context context){ return (AppApplication)context.getApplicationContext(); } private AppComponent appComponent; @Override public void onCreate() { super.onCreate(); appComponent=DaggerAppComponent.builder() .apiServiceModule(new ApiServiceModule()) .build();
}
public AppComponent getAppComponent() { return appComponent; }
}</pre>
那么現在只要在任何地方想要獲取 ApiService 的實例,只要得到了 AppApplication 的實例(AppApplication.get(), 就可以通過 AppAppLication 的 getAppComponent() 方法得到 AppComponent ,然后通過 AppComponent 就可以間接得到 ApiService 的實例了。
上面大概說了下在 Application 中應用 Dagger2 去實例化 AppComponent,其實在 Activity 使用也是同樣的道理。現在暫時沒時間 寫在 Activity 中怎么使用 Dagger2 了,具體可以從代碼中查看。
代碼都已經在 Github ,歡迎 查閱
原文 http://maoruibin.xyz/technology/2015/12/21/mvp_dagger2.html