Dagger2 與 MVP
Dagger2是Google提供的依賴注入框架膏燃,依賴注入為Android中組件之間的解耦提供了很好的解決方案捆昏。Dagger2已經(jīng)在越來越多的開源項(xiàng)目中被使用,其已經(jīng)發(fā)展成為未來的一個(gè)技術(shù)趨勢店归。
MVP設(shè)計(jì)模式大家也非常熟悉听绳,其概念不在此處贅述。MVP的實(shí)現(xiàn)方式非常多樣坯门,和Dagger2相結(jié)合正式本文要論述和討論的。
MVP模式將原來一個(gè)Activity可以解決的問題逗扒,分成了M古戴、V、P三部分矩肩,這在項(xiàng)目中會產(chǎn)品大量的類现恼,如果利用Dagger2來進(jìn)行這些類的有效管理,是本文思考的問題黍檩。本文將從Dagger2基礎(chǔ)論述叉袍,MVP的實(shí)現(xiàn)方式以及Dagger2和MVP的結(jié)合,三個(gè)方面來討論刽酱,希望對大家有用喳逛。
Dagger2 基礎(chǔ)
Dagger2主要基于注解來實(shí)現(xiàn),剛接觸Dagger2的時(shí)候都會有這樣的困惑:
- Inject棵里,Component润文,Module姐呐,Provides,Scope典蝌,Qualifier是什么鬼曙砂?
- Dagger2如何應(yīng)用到項(xiàng)目中?
Inject
Inject,即注入骏掀,該注解標(biāo)示地方表示需要通過DI框架來注入實(shí)例麦轰。Inject有三種方式,分別是Constructor injection砖织、Fields injection款侵、Methods injection。申明了Inject之后侧纯,會從注入框架中去查找需要注入的類實(shí)例新锈,然后注入進(jìn)來,也就是通過Component去查找眶熬。這三種注入的表現(xiàn)形式如下:
public class SolutionCreatePresenterImpl implements SolutionCreatePresenter {
...
@Inject
public SolutionCreatePresenterImpl(Activity activity, StudioApiService apiService, RxBus rxBus, DataCenter dataCenter) {
this.activity = activity;
this.apiService = apiService;
this.rxBus = rxBus;
...
}
}
public class SolutionCreateActivity extends BasePresenterActivity implements SolutionCreateView {
@Inject
SolutionCreatePresenter solutionCreatePresenter;
@Inject
RxBus rxBus;
@Inject
DataCenter dataCenter;
@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState {
super.onCreate(savedInstanceState);
component().inject(this);
...
}
}
public class LoginActivityPresenter {
private LoginActivity loginActivity;
@Inject
public LoginActivityPresenter(LoginActivity loginActivity) {
this.loginActivity = loginActivity;
}
@Inject
public void enableWatches(Watches watches) {
watches.register(this); //Watches instance required fully constructed LoginActivityPresenter
}
}
Component
Component 要解決的問題就是Inject的實(shí)例從哪里來妹笆,所以它承擔(dān)的就是一個(gè)連接器的作用。Component需要引用到目標(biāo)類的實(shí)例娜氏,Component會查找目標(biāo)類中用Inject注解標(biāo)注的屬性拳缠,查找到相應(yīng)的屬性后會接著查找該屬性對應(yīng)的用Inject標(biāo)注的構(gòu)造函數(shù)(這時(shí)候就發(fā)生聯(lián)系了),剩下的工作就是初始化該屬性的實(shí)例并把實(shí)例進(jìn)行賦值贸弥。
Module
Module 是類實(shí)例提供的工廠模式窟坐,Module里面的方法基本都是創(chuàng)建類實(shí)例的方法。這樣绵疲,Dagger2中就有2個(gè)維度可以創(chuàng)建類實(shí)例:
- 通過用Inject注解標(biāo)注的構(gòu)造函數(shù)來創(chuàng)建(以下簡稱Inject維度)
- 通過工廠模式的Module來創(chuàng)建(以下簡稱Module維度)
Provides
Provides 用在Module中哲鸳,聲明方法可以返回dependencies,也就是方法可以提供被注入的類實(shí)例盔憨。
@Module
public class NetApiModule {
@Provides
@Singleton
StudioApiService provideStudioApiService(RestApi restApi) {
return restApi.retrofitStudio(GlobalConfig.STUDIO_API_BASE_URL).create(StudioApiService.class);
}
@Provides
@Singleton
RxBus provideRxBus() {
return RxBus.getInstance();
}
}
如下所以徙菠,NetApiModule提供了StudioAPIService、RxBus類的依賴實(shí)例郁岩。
Scope
Dagger2中Scope關(guān)心的問題就是類實(shí)例的生命周期婿奔,@ApplicationScope 是希望類實(shí)例和Application一樣,也就是唯一單例问慎;@ActivityScope則希望類實(shí)例和Activity生命周期一致萍摊。如此,Scope就有“l(fā)ocal singletons” 的概念了蝴乔。舉個(gè)例子:AppComponent 用 @Singleton 進(jìn)行標(biāo)識记餐,由于AppComponent只在Application中創(chuàng)建一次,這就保證了AppComponent所引用的ApplicationModule 中用 @Singleton 標(biāo)識的類都是單例薇正;同樣的片酝,ActivityComponent 用 @ActivityScope進(jìn)行標(biāo)識,每次創(chuàng)建新的Activity都會創(chuàng)建一個(gè)ActivityComponent挖腰,這就是的ActivityComponent所引用的ActivityModule中用@ActivityScope 標(biāo)識的類都具有和Activity一樣的生命周期雕沿。
在后面的講解中,讀者可以去體會Scope標(biāo)識后的猴仑,類實(shí)例和Component的相互依賴關(guān)系审轮。
Qualifier
如果類實(shí)例創(chuàng)建有多種相同的方式,就需要通過標(biāo)簽tag來區(qū)分之辽俗,并在注入的時(shí)候通過標(biāo)簽來區(qū)分疾渣。
Dagger2如何應(yīng)用到項(xiàng)目中
Dagger2的使用首先要擺脫傳統(tǒng)的類創(chuàng)建模式,即摒棄各種new的使用方式崖飘,要有類倉庫的概念榴捡,所有類的引用采用Inject來注入。其次朱浴,要組織好Component吊圾,Component是連接器,提供了不同的類注入翰蠢,有為Application提供的项乒,有為Activity提供的,按照個(gè)人經(jīng)驗(yàn)來講梁沧,Component的劃分有一下規(guī)則:
- 要有全局Component, 負(fù)責(zé)管理整個(gè)app的全局類實(shí)例, 這些類基本都是單例模式檀何,一般都用@Singleton標(biāo)識
- 每個(gè)頁面對應(yīng)一個(gè)Component,即每個(gè)Activity 和 Fragment廷支,谷歌官方Dagger2示例是采用這種方式埃碱,這種方式有個(gè)不好地方,Component太多
- 將頁面依賴的類抽出酥泞,公用一個(gè)Component砚殿,有特殊需要的時(shí)候繼承該公有Component
- 不同類型的Component及Module,采用Scope進(jìn)行區(qū)分
參考鏈接:
Miroslaw Stanek: http://frogermcs.github.io/dependency-injection-with-dagger-2-the-api/
Google: https://github.com/google/dagger
MVP 實(shí)現(xiàn)探討
MVP的實(shí)現(xiàn)方式很多芝囤,這里介紹兩種:
- 以Activity和Fragment作為View(視圖層)似炎,Presenter管理業(yè)務(wù)邏輯;
- 使用Activity和Fragment作為presenters悯姊,View單獨(dú)抽出處理視圖相關(guān)羡藐。
使用Activity和Fragment作為View
這種方式是比較傳統(tǒng)的實(shí)現(xiàn)方式,也比較符合我們的習(xí)慣悯许,因?yàn)橹岸际窃贏ctivity里面處理View相關(guān)仆嗦,改用MVP模式也只是也Activity里面的業(yè)務(wù)邏輯抽成Presenter。這里要提的是Google官方MVP也是采用這種方式先壕。
下面說一下我的實(shí)現(xiàn)方式瘩扼,將Presenter和View都抽出一個(gè)基類接口谆甜,每個(gè)Activity和Fragment都分別定義對應(yīng)的Presenter和View接口,并集成基類接口集绰。具體實(shí)現(xiàn)如下:
基類接口View
public interface LoadDataView {
/**
* Show a view with a progress_upload bar indicating a loading process.
*/
void showLoading();
/**
* Hide a loading view.
*/
void hideLoading();
/**
* Show a retry view in case of an error when retrieving data.
*/
void showRetry();
/**
* Hide a retry view shown if there was an error when retrieving data.
*/
void hideRetry();
/**
* Show an error message
*
* @param message A string representing an error.
*/
void showError(String message);
/**
* Get a {@link Context}.
*/
Context context();
}
基類接口Presenter
public interface Presenter<T> {
/**
* Method that control the lifecycle of the view. It should be called in the view's
* (Activity or Fragment) onStart() method.
*/
void start();
/**
* Method that control the lifecycle of the view. It should be called in the view's
* (Activity or Fragment) onDestroy() method.
*/
void destroy();
void setView(T view);
}
我們以本文之前用到的SolutionCreatePresenter來講述具體頁面的Presenter定義规辱。
// 接口定義
public interface SolutionCreatePresenter extends
Presenter<SolutionCreateView>, View.OnClickListener, View.OnFocusChangeListener {
Solution getSolution();
void setSolution(Solution solution);
}
// 接口實(shí)現(xiàn)
public class SolutionCreatePresenterImpl implements SolutionCreatePresenter {
...
@Inject
public SolutionCreatePresenterImpl(Activity activity, StudioApiService apiService, RxBus rxBus, DataCenter dataCenter) {
this.activity = activity;
this.apiService = apiService;
this.rxBus = rxBus;
...
}
@Override
public void setView(SolutionCreateView view) {
this.solutionCreateView = view;
}
// 繼續(xù)添加其他 override 方法
}
SolutionCreateActivity 見本文前面的代碼,需要implements SoluionCreateView, 并且Inject SolutionPresenter.
使用Activity和Fragment作為Presenters
為何要采用這種方式呢栽燕,基于兩點(diǎn)來考慮:
- Activity Fragment本身就有生命周期的管理罕袋,這種管理類似于業(yè)務(wù)邏輯,所以要?dú)w為Presenter碍岔;
- Activity Fragment生命周期變化時(shí)浴讯,會帶來業(yè)務(wù)邏輯的變化,直接作為Presenter蔼啦,可以避免業(yè)務(wù)邏輯的復(fù)雜榆纽。
個(gè)中意味,讀者自己體驗(yàn)體驗(yàn)询吴,當(dāng)自己有需求時(shí)掠河,可以根據(jù)這兩種方式自由變換。
參考鏈接:
一種在android中實(shí)現(xiàn)MVP模式的新思路:
https://github.com/hehonghui/android-tech-frontier/tree/master/androidweekly/一種在android中實(shí)現(xiàn)MVP模式的新思路
Google Samples:
https://github.com/googlesamples/android-architecture
Dagger2 與 MVP 的結(jié)合
這一部分猛计,重點(diǎn)介紹Component 和 Module的設(shè)計(jì)唠摹,目的在于更好適應(yīng)MVP模式。
基本思路:
- 全局Component通過AppComponent進(jìn)行管理奉瘤,大多設(shè)置單例模式勾拉;
- 將Activity和Fragment Component中通用的抽出,為BaseViewComponent盗温;
- 上層PresenterComponent繼承BaseViewComponet藕赞,DataBandingComponent只能繼承android.databinding.DataBindingComponent,但可以將BaseViewModule 包含進(jìn)來卖局。其他Component使用時(shí)可以繼承BaseViewComponent斧蜕。
架構(gòu)圖如下:
核心代碼如下:
AppComponent
@Singleton
@Component(modules = {AppModule.class, AppManageModule.class})
public interface AppComponent {
void inject(DajiaApplication app);
}
AppModule
@Module(includes = NetApiModule.class)
public class AppModule {
private final Application application;
public AppModule(Application app) {
application = app;
}
@Provides
@Singleton
Application provideApplication() {
return application;
}
@Provides
@Singleton
Context provideContext() {
return application;
}
}
BaseViewComponent
@Scope
@Retention(RUNTIME)
public @interface PerView {
}
@PerView
@Component(dependencies = AppComponent.class, modules = BaseViewModule.class)
public interface BaseViewComponent {
Activity activity();
void inject(AbstractActivity activity);
void inject(BaseFragment fragment);
}
BaseViewModule
@Module
public class BaseViewModule {
private final Activity activity;
public BaseViewModule(Activity activity) {
this.activity = activity;
}
@Provides
@PerView
Activity provideActivity() {
return this.activity;
}
}
PresenterComponent
@PerView
@Component(dependencies = AppComponent.class, modules = {
BaseViewModule.class, PresenterModule.class
})
public interface PresenterComponent extends BaseViewComponent {
void inject(SolutionCreateActivity activity);
}
PresenterModule
@Module
public class PresenterModule {
@Provides
@PerView
SolutionCreatePresenter provideSolutionCreatePresenter(SolutionCreatePresenterImpl presenter) {
return presenter;
}
}
此外,可以將Activity和Fragment中公用的東西抽出 AbstractActivity砚偶、BaseActivity批销、BasePresenterActiviy以及BaseFragment,并在這些類中初始化對應(yīng)的Component染坯。以AbstractActivity 和 BasePresenterActiviy為例:
public class AbstractActivity extends AppCompatActivity {
private BaseViewComponent mBaseViewComponent;
public BaseViewComponent component() {
if (mBaseViewComponent == null) {
mBaseViewComponent = DaggerBaseViewComponent.builder()
.appComponent(DajiaApplication.getInstance().component())
.baseViewModule(new BaseViewModule(this))
.build();
}
return mBaseViewComponent;
}
}
public class BasePresenterActivity extends BaseActivity {
private PresenterComponent presenterComponent;
public PresenterComponent component() {
if (presenterComponent == null) {
presenterComponent = DaggerPresenterComponent.builder()
.appComponent(DajiaApplication.getInstance().component())
.baseViewModule(new BaseViewModule(this))
.presenterModule(new PresenterModule()).build();
}
return presenterComponent;
}
}
如此均芽,在使用MVP時(shí),遵循如下步驟:
- 創(chuàng)建新的Presenter 繼承 BasePresenter;
- 實(shí)現(xiàn)該接口单鹿,PresenterImpl掀宋;
- 在PresenterModule中提供PresenterImpl的注入方法;
- 創(chuàng)建新的View,繼承BaseView劲妙;
- 創(chuàng)建新的頁面Activity或者Frament湃鹊,implements View;
- PresenterComponent添加inject方法是趴,在新的頁面中inject Presenter涛舍。
結(jié)語
本文想要系統(tǒng)的描述Dagger2和MVP的架構(gòu)思想澄惊,但是文筆有限唆途,行文中還是感覺有很多描述不到位的地方,希望讀者在閱讀時(shí)把握核心思想掸驱,而不局限于具體的實(shí)現(xiàn)步驟肛搬。