開發(fā)一個App,和起房子應該有異曲同工之處,起房子需要畫好設計圖紙,而我們開發(fā)App則需要先設計好App整個架構(gòu)模式。目前Android一般有MVC、MVP和MVVM,本文則先來說說MVP架構(gòu)。在了解MVP架構(gòu)之前,有人可能會說,MVP架構(gòu)是不是有點落后了,但是我想說,如果你公司有老項目,他就是用MVP架構(gòu)寫的,這時候我們MVP知識是不是就派上用場了;任何架構(gòu)都有它存在的理由,學習架構(gòu)的思想才是關(guān)鍵。MVP分別代表Model、View、Presenter三個英文字母,和傳統(tǒng)的MVC 相比,C替換成了P。Presenter英文單詞有主持人意思,也就是說Presenter是View 和 Model 的主持人,按照慣例我們先來看兩張圖。
MVC MVP 架構(gòu)對比圖
mvc
mvc
mvp
mvp
- 通過以上兩張圖對比,MVC在Android中就是我們剛開始學習Android時輸出Android代碼的真實寫照,Activity不僅負責顯示View,它還是Controller,我們可以在Activity開始網(wǎng)絡請求,請求完成更新UI,也可以在Activity中通過UI組件獲取用戶輸入數(shù)據(jù),然后執(zhí)行網(wǎng)絡請求再更新UI,這樣一來,一個功能復雜的頁面一個Activity三四千行代碼是很常見的事情,這也會導致后面維護代碼人來讀你的Activity代碼可能會直接崩潰,同時代碼的耦合度也很高。
- 而我們再看MVP架構(gòu),這就會很清晰,它把MVC中的VC進行解耦,也就是說把Activity中的UI邏輯抽象成View 接口 ,把業(yè)務邏輯抽象成 presenter 接口, model 還是原來的model,這樣其實就呼應了文章我們所說presenter主持人的意思,model 更新UI需要通過presenter,view 更新model數(shù)據(jù)也需要通過presenter,相當于presenter主持大局。
- 說了這么多,其實MVC和MVP的區(qū)別可以用一句話代替,那就是View能否直接操作Model,接下來我們就看看MVP架構(gòu)如何在Android中實踐。
Android 實現(xiàn) mvp 架構(gòu)
UI邏輯抽象成View接口
/**
* @author maoqitian
* @Description View 的基類
*/
public interface BaseView {
/**
* 正常顯示
*/
void showNormal();
/**
* 顯示錯誤
*/
void showError();
/**
* 正在加載
*/
void showLoading();
/**
* 顯示錯誤信息
* @param errorMsg 錯誤信息
*/
void showErrorMsg(String errorMsg);
}
業(yè)務邏輯抽象成 Presenter 接口
- 抽象之前我們可以想一想,每個presenter都對應一個View 界面,所以我們需要一個方法來綁定對應的View,綁定的目的是為了方便我們在presenter中更新view,當界面銷毀的時候也需要一個方法類解綁View。此外,界面肯定不止一個,并且肯定實現(xiàn)前面我們寫的BaseView接口,我們用泛型代替,就有了如下BasePresenter 接口
/**
* @author maoqitian
* @Description Presenter 基類接口
*/
public interface AbstractBasePresenter<T extends BaseView> {
/**
* 綁定View
* @param view
*/
void attachView(T view);
/**
* 解綁View
*/
void detachView();
}
MVP工作流
- 前面我們已經(jīng)抽象出了View、Presenter接口,接下來從結(jié)合文章開頭MVP architectural pattern圖從用戶打開App獲取數(shù)據(jù)開始展現(xiàn)整體MVP工作流。
View 與 Presenter 結(jié)合
View 獲取Presenter對象
- View 獲取數(shù)據(jù)需要通過Presenter對象,view在Android 中一般代表Avtivity、或者Fragment。先創(chuàng)建Avtivity和Fragment抽象類類做基礎封裝。
/**
* @author maoqitian
* @Description activity基類
*/
public abstract class AbstractActivity extends AppCompatActivity{
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(getLayout());
onViewCreated();
initToolbar();
initEventAndData();
}
/**
* view 的創(chuàng)建 留給子類實現(xiàn)
*/
protected abstract void onViewCreated();
/**
* 初始化 toolbar
*/
protected abstract void initToolbar();
/**
* 初始化數(shù)據(jù)留給子類實現(xiàn)
*/
protected abstract void initEventAndData();
/**
* 獲取布局對象 留給子類實現(xiàn)
*/
protected abstract int getLayout();
}
- 接著我們實現(xiàn)MVP Activity基類
/**
* @author maoqitian
* @Description MVP BaseActivity 基類
*/
public abstract class BaseActivity <T extends AbstractBasePresenter> extends AbstractActivity implements BaseView{
protected T mPresenter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
mPresenter = createPresenter();
super.onCreate(savedInstanceState);
}
@Override
protected void onViewCreated() {
if (mPresenter != null) {
mPresenter.attachView(this);
}
}
@Override
protected void onDestroy() {
super.onDestroy();
if(mPresenter != null){
mPresenter.detachView();
mPresenter = null;
}
}
/**
* 創(chuàng)建Presenter
*/
protected abstract T createPresenter();
@Override
public void showNormal() {
}
@Override
public void showError() {
}
@Override
public void showLoading() {
}
@Override
public void showErrorMsg(String errorMsg) {
}
}
- 到此,只要我們界面繼承BaseActivity,并且實現(xiàn)createPresenter方法,我們就可以很直接在View中通過Presenter來獲獲取數(shù)據(jù),如何獲取呢?接著往下看。
Presenter 獲取View 對象
- 現(xiàn)在我們創(chuàng)建一個Presenter基類將其與View結(jié)合,為后續(xù)步驟做準備,注意我們RxBasePresenter基類構(gòu)造方法中需要傳入DataClient,該類其實就可以概括代表Module。
/**
* @author maoqitian
* @Description 基于Presenter封裝
*/
public class RxBasePresenter<T extends BaseView> implements AbstractBasePresenter<T>{
protected T mView;
private DataClient mDataClient;
public RxBasePresenter(DataClient dataClient){
this.mDataClient=dataClient;
}
@Override
public void attachView(T view) {
this.mView=view;
}
@Override
public void detachView() {
this.mView = null;
}
}
Presenter 與 View 之間連接
- 當我們創(chuàng)建View 對應Presenter讓其繼承 RxBasePresenter,則 Presenter便可以執(zhí)行Updates view,如何操作呢?我們可以通過接口來進行數(shù)據(jù)獲取與顯示的擴展。以下舉個例子
public interface MainContract {
interface MainView extends BaseView{
void showMainData();
}
interface MainActivityPresenter extends AbstractBasePresenter<MainView>{
void getMainData();
}
}
- 至此,我們基本MVP架構(gòu)其實就已經(jīng)搭建完成,我們來看看使用
/**
* @author maoqitian
* @Description MainPresenter (Presenter)
*/
public class MainPresenter extends RxBasePresenter<MainContract.MainView> implements MainContract.MainActivityPresenter {
//(Model)
private DataClient mDataClient;
public MainPresenter(DataClient dataClient) {
super(dataClient);
this.mDataClient = dataClient;
}
@Override
public void attachView(MainContract.MainView view) {
super.attachView(view);
}
//獲取數(shù)據(jù)
@Override
public void getMainData() {
//mDataClient 網(wǎng)絡請求獲取數(shù)據(jù)
.......
// 數(shù)據(jù)獲取成功展示數(shù)據(jù)
mView.showMainData();
}
}
/**
* @author maoqitian
* @Description MainActivity (View)
*/
public class MainActivity extends BaseActivity<MainPresenter>implements MainContract.MainView{
..........
@Override
protected MainPresenter createPresenter() {
return new MainPresenter(new DataClient());
}
@Override
protected void initEventAndData() {
mPresenter.getMainData();
}
@Override
public void showMainData() {
//顯示數(shù)據(jù)
}
}
- 通過以上示例代碼,再次對比文章開頭的Android MVP architectural pattern圖,從用戶打開App獲取數(shù)據(jù)開始展現(xiàn)整體MVP工作流已經(jīng)走完。
谷歌官方示例MVP demo
- 當然上面只是簡單的講解了在Android中搭建基本MVP架構(gòu),其實谷歌官方也給我提供了MVP示例代碼,具體代碼可以自行去了解。
- 谷歌官方MVP示例Demo
使用dagger2優(yōu)化MVP 架構(gòu)
- 前面我們大致搭建了一個基礎的MVP架構(gòu),每個Presnter需要我們在View 中去創(chuàng)建,創(chuàng)建Presnter的時候還需要傳入Model,這就說明他們之間的解耦還不夠。這里我們換一種思路,在原有基礎,不管是DataClient(Model)還是對應的Presnter都可以直接提供對應的對象,然后對應的類創(chuàng)建我們就將其注入,這樣不就省去了對象創(chuàng)建,Model、Presnter、View 之間耦合度就進一步降低,如何實現(xiàn)?還是強大的谷歌爸爸給我們提供了方案,使用Dagger2(Dagger是一個完全靜態(tài)的編譯時依賴注入框架,適用于Java和Android)。
項目添加對應dagger依賴
- 使用dagger對應版本為2.22.1
dependencies {
implementation 'com.google.dagger:dagger:2.22.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
//
implementation 'com.google.dagger:dagger-android:2.22.1'
implementation 'com.google.dagger:dagger-android-support:2.22.1'
annotationProcessor 'com.google.dagger:dagger-android-processor:2.22.1'
}
改造Presnter類
- 這里我們以上面例子中MainPresenter為例,在其構(gòu)造方法添加@Inject注解,表明Dagger2 可以從這獲取對應MainPresenter實例,注意構(gòu)造方法中需要DataClient對象,這里也使用Dagger來提供對象(稍后再說)
public class MainPresenter extends RxBasePresenter<MainContract.MainView> implements MainContract.MainActivityPresenter {
private DataClient mDataClient;
//@Inject注解表示Dagger2 可以從這獲取Presenter 實例
@Inject
public MainPresenter(DataClient dataClient) {
super(dataClient);
this.mDataClient = dataClient;
}
}
改造View
- View 中結(jié)合 Dagger2 本應該繼承 DaggerAppCompatActivity,但是我們基類為AbstractActivity,查看DaggerAppCompatActivity源碼,直接手動實現(xiàn)DaggerAppCompatActivity中代碼。
/**
* @author maoqitian
* @Description MVP BaseActivity 基類
*/
public abstract class BaseActivity <T extends AbstractBasePresenter> extends AbstractSimpleActivity implements BaseView, HasFragmentInjector,HasSupportFragmentInjector {
//Presenter 對象注入 (注意不能使用 private )
@Inject
protected T mPresenter;
//手動實現(xiàn)DaggerAppCompatActivity功能
@Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
@Inject DispatchingAndroidInjector<android.app.Fragment> frameworkFragmentInjector;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
//必須在super.onCreate之前調(diào)用AndroidInjection.inject
AndroidInjection.inject(this);
super.onCreate(savedInstanceState);
}
@Override
public AndroidInjector<Fragment> supportFragmentInjector() {
return supportFragmentInjector;
}
@Override
public AndroidInjector<android.app.Fragment> fragmentInjector() {
return frameworkFragmentInjector;
}
}
添加Dagger Module和Component
創(chuàng)建MainActivityModule
- 抽象類MainActivityModule加入@Module注解,并添加返回我們對應MainActivityPresenter接口的抽象方法,@Binds注解就可以幫我們把MainActivityPresenter接口綁定到MainPresenter上。
/**
* @author maoqitian
* @Description MainActivity 可以提供的注入對象Module
* @Time 2019/3/27 0027 23:59
*/
@Module
public abstract class MainActivityModule {
@ActivityScope
@Binds
abstract MainContract.MainActivityPresenter bindPresenter(MainPresenter presenter);
}
創(chuàng)建用于生成Activity注入器的Module類
/**
* @author maoqitian
* @Description 用于生成Activity注入器的Module,使用@ContributesAndroidInjector注解并指定modules為
* @Time 2019/4/14 0014 14:09
*/
@Module
public abstract class ActivityBindingModule {
@ActivityScope
@ContributesAndroidInjector(modules = MainActivityModule.class)
abstract MainActivity contributeMainActivity();
}
創(chuàng)建提供我們需要對象的Module類
- 與前文對應,這里我們提供了對應了DataClient對象,也就是MVP中的Model,在注入Presenter的時候?qū)⑵湟黄鹱⑷搿?/li>
@Module
public class MyAppModule {
@Provides
@Singleton
public DataClient providerDataClient(){
return new DataClient();
}
}
使用@Component注解創(chuàng)建AppCompontent接口類
- Dagger會幫我們自動生成DaggerAppComponent類,繼承自AndroidInjector并指定我們自己的Application類,指定AndroidSupportInjectionModule幫助把Android中四大組件以及Fragment進行綁定,@Singleton注解指定單例
@Singleton
@Component(modules = {
MyAppModule.class,
ActivityBindingModule.class,
AndroidSupportInjectionModule.class
})
public interface AppComponent extends AndroidInjector<MyApplication> {
}
改造Application類繼承自DaggerApplication
- 按照如下改造MyApplication之后我們從新編譯編譯一下代碼,如果編譯通過,dagger就會幫我們生成對應DaggerAppComponent.create()方法,將其返回在applicationInjector()方法中。
public class MyApplication extends DaggerApplication {
@Override
protected AndroidInjector<? extends DaggerApplication> applicationInjector() {
return DaggerAppComponent.create();
}
}
- 項目編譯通過dagger會在build目錄生成對應對象注入類,具體源碼以后再出文章分析,這里就先告一段落了。到此,使用dagger2優(yōu)化MVP 架構(gòu)基本完成了,但是還有其他細節(jié)這里沒有提及,比如每個Presenter之間該如何通信,可以使用EventBus,也可以Rxbus等等,具體細節(jié)可以看接下架構(gòu)實踐中我寫的開源項目的代碼。

buildgenerated.png
架構(gòu)實踐
項目介紹
- 通過前面對MVP架構(gòu)分析介紹,接下來我給大家推薦我的一個開源項目,這是一款有較好用戶體驗的開源玩Android客戶端。提供豐富完整的功能,更好的體驗,旨在更好的瀏覽https://www.wanandroid.com/網(wǎng)站內(nèi)容,更好的在手機上進行學習。項目使用Retrofit2 + RxJava2 + Dagger2 +MVP+RxBus架構(gòu),盡量使用Material Design控件進行開發(fā)。
項目架構(gòu)圖
MVP-WanAndroid-Architecture
項目地址
總結(jié)
- 以前所說的知識一種MVP架構(gòu)的寫法,我們也可以根據(jù)自己理解寫出不一樣的MVP,其實MVP架構(gòu)看似不錯,但也還是會有缺點,那就是寫一個頁面會產(chǎn)生很多個類,雖然結(jié)構(gòu)清晰,但是要寫的代碼變多了,凡事都會有利弊。如果你不想自己寫這么多的類,github上也有大神寫好了輪子(MVPArms)專門幫我們生成MVP架構(gòu)的框架,但是用框架生成代碼總歸是別人的,只有自己擼一遍,把邏輯流程梳理清楚才會變成自己的東西,才會成長。文章中如果有錯誤,請大家給我提出來,大家一起學習進步,如果覺得我的文章給予你幫助,也請給我一個喜歡和關(guān)注,同時也歡迎訪問我的個人博客。