Android 基本架構(gòu)之MVP分析與實踐

開發(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

項目地址

https://github.com/maoqitian/MaoWanAndoidClient

總結(jié)

  • 以前所說的知識一種MVP架構(gòu)的寫法,我們也可以根據(jù)自己理解寫出不一樣的MVP,其實MVP架構(gòu)看似不錯,但也還是會有缺點,那就是寫一個頁面會產(chǎn)生很多個類,雖然結(jié)構(gòu)清晰,但是要寫的代碼變多了,凡事都會有利弊。如果你不想自己寫這么多的類,github上也有大神寫好了輪子(MVPArms)專門幫我們生成MVP架構(gòu)的框架,但是用框架生成代碼總歸是別人的,只有自己擼一遍,把邏輯流程梳理清楚才會變成自己的東西,才會成長。文章中如果有錯誤,請大家給我提出來,大家一起學習進步,如果覺得我的文章給予你幫助,也請給我一個喜歡和關(guān)注,同時也歡迎訪問我的個人博客。

About me

blog:

mail:

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容