Android 基本架構之MVP分析與實踐 MVC MVP 架構對比圖 Android 實現 mvp 架構 MVP工作流 使用dagger2優化MVP 架構 架構實踐 總結 About me

開發一個App,和起房子應該有異曲同工之處,起房子需要畫好設計圖紙,而我們開發App則需要先設計好App整個架構模式。目前Android一般有MVC、MVP和MVVM,本文則先來說說MVP架構。在瞭解MVP架構之前,有人可能會說,MVP架構是不是有點落後了,但是我想說,如果你公司有老項目,他就是用MVP架構寫的,這時候我們MVP知識是不是就派上用場了;任何架構都有它存在的理由,學習架構的思想纔是關鍵。MVP分別代表Model、View、Presenter三個英文字母,和傳統的MVC 相比,C替換成了P。Presenter英文單詞有主持人意思,也就是說Presenter是View 和 Model 的主持人,按照慣例我們先來看兩張圖。

MVC MVP 架構對比圖

mvc

mvp

  • 通過以上兩張圖對比,MVC在Android中就是我們剛開始學習Android時輸出Android代碼的真實寫照,Activity不僅負責顯示View,它還是Controller,我們可以在Activity開始網絡請求,請求完成更新UI,也可以在Activity中通過UI組件獲取用戶輸入數據,然後執行網絡請求再更新UI,這樣一來,一個功能複雜的頁面一個Activity三四千行代碼是很常見的事情,這也會導致後面維護代碼人來讀你的Activity代碼可能會直接崩潰,同時代碼的耦合度也很高。
  • 而我們再看MVP架構,這就會很清晰,它把MVC中的VC進行解耦,也就是說把Activity中的UI邏輯抽象成View 接口 ,把業務邏輯抽象成 presenter 接口, model 還是原來的model,這樣其實就呼應了文章我們所說presenter主持人的意思,model 更新UI需要通過presenter,view 更新model數據也需要通過presenter,相當於presenter主持大局。
  • 說了這麼多,其實MVC和MVP的區別可以用一句話代替,那就是View能否直接操作Model,接下來我們就看看MVP架構如何在Android中實踐。

Android 實現 mvp 架構

UI邏輯抽象成View接口

/**
 * @author maoqitian
 * @Description View 的基類
 */
public interface BaseView {

    /**
     * 正常顯示
     */
    void showNormal();

    /**
     * 顯示錯誤
     */
    void showError();

    /**
     * 正在加載
     */
    void showLoading();
    /**
     * 顯示錯誤信息
     * @param errorMsg 錯誤信息
     */
    void showErrorMsg(String errorMsg);
}

業務邏輯抽象成 Presenter 接口

  • 抽象之前我們可以想一想,每個presenter都對應一個View 界面,所以我們需要一個方法來綁定對應的View,綁定的目的是爲了方便我們在presenter中更新view,當界面銷燬的時候也需要一個方法類解綁View。此外,界面肯定不止一個,並且肯定實現前面我們寫的BaseView接口,我們用泛型代替,就有了如下BasePresenter 接口
/**
 * @author maoqitian
 * @Description Presenter 基類接口
 */
public interface AbstractBasePresenter<T extends BaseView> {

    /**
     * 綁定View
     * @param view
     */
    void attachView(T view);

    /**
     * 解綁View
     */
    void detachView();
}

MVP工作流

  • 前面我們已經抽象出了View、Presenter接口,接下來從結合文章開頭MVP architectural pattern圖從用戶打開App獲取數據開始展現整體MVP工作流。

View 與 Presenter 結合

View 獲取Presenter對象

  • View 獲取數據需要通過Presenter對象,view在Android 中一般代表Avtivity、或者Fragment。先創建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 的創建 留給子類實現
     */
    protected abstract void onViewCreated();
    /**
     * 初始化 toolbar
     */
    protected abstract void initToolbar();
    /**
     * 初始化數據留給子類實現
     */
    protected abstract void initEventAndData();

    /**
     * 獲取佈局對象 留給子類實現
     */
    protected abstract int getLayout();

}
  • 接着我們實現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;
        }
    }

    /**
     * 創建Presenter
     */
    protected abstract T createPresenter();

    @Override
    public void showNormal() {

    }

    @Override
    public void showError() {

    }

    @Override
    public void showLoading() {

    }

    @Override
    public void showErrorMsg(String errorMsg) {

    }
}    
  • 到此,只要我們界面繼承BaseActivity,並且實現createPresenter方法,我們就可以很直接在View中通過Presenter來獲獲取數據,如何獲取呢?接着往下看。

Presenter 獲取View 對象

  • 現在我們創建一個Presenter基類將其與View結合,爲後續步驟做準備,注意我們RxBasePresenter基類構造方法中需要傳入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 之間連接

  • 當我們創建View 對應Presenter讓其繼承 RxBasePresenter,則 Presenter便可以執行Updates view,如何操作呢?我們可以通過接口來進行數據獲取與顯示的擴展。以下舉個例子
public interface MainContract {

    interface MainView extends BaseView{
        void showMainData();
    }


    interface MainActivityPresenter extends AbstractBasePresenter<MainView>{
        void getMainData();
    }
}
  • 至此,我們基本MVP架構其實就已經搭建完成,我們來看看使用
/**
 * @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);
        
    }
    //獲取數據
    @Override
    public void getMainData() {
        //mDataClient 網絡請求獲取數據
        .......
        // 數據獲取成功展示數據
        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() {
    //顯示數據
    }   
    
}
  • 通過以上示例代碼,再次對比文章開頭的Android MVP architectural pattern圖,從用戶打開App獲取數據開始展現整體MVP工作流已經走完。

谷歌官方示例MVP demo

  • 當然上面只是簡單的講解了在Android中搭建基本MVP架構,其實谷歌官方也給我提供了MVP示例代碼,具體代碼可以自行去了解。
  • 谷歌官方MVP示例Demo

使用dagger2優化MVP 架構

  • 前面我們大致搭建了一個基礎的MVP架構,每個Presnter需要我們在View 中去創建,創建Presnter的時候還需要傳入Model,這就說明他們之間的解耦還不夠。這裏我們換一種思路,在原有基礎,不管是DataClient(Model)還是對應的Presnter都可以直接提供對應的對象,然後對應的類創建我們就將其注入,這樣不就省去了對象創建,Model、Presnter、View 之間耦合度就進一步降低,如何實現?還是強大的谷歌爸爸給我們提供了方案,使用Dagger2(Dagger是一個完全靜態的編譯時依賴注入框架,適用於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爲例,在其構造方法添加@Inject註解,表明Dagger2 可以從這獲取對應MainPresenter實例,注意構造方法中需要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 中結合 Dagger2 本應該繼承 DaggerAppCompatActivity,但是我們基類爲AbstractActivity,查看DaggerAppCompatActivity源碼,直接手動實現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;
    //手動實現DaggerAppCompatActivity功能
    @Inject DispatchingAndroidInjector<Fragment> supportFragmentInjector;
    @Inject DispatchingAndroidInjector<android.app.Fragment> frameworkFragmentInjector;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        //必須在super.onCreate之前調用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

創建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);
}

創建用於生成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();
    
}

創建提供我們需要對象的Module類

  • 與前文對應,這裏我們提供了對應了DataClient對象,也就是MVP中的Model,在注入Presenter的時候將其一起注入。
@Module
public class MyAppModule {

    @Provides
    @Singleton
    public DataClient providerDataClient(){
        return new DataClient();
    }
}

使用@Component註解創建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優化MVP 架構基本完成了,但是還有其他細節這裏沒有提及,比如每個Presenter之間該如何通信,可以使用EventBus,也可以Rxbus等等,具體細節可以看接下架構實踐中我寫的開源項目的代碼。

架構實踐

項目介紹

  • 通過前面對MVP架構分析介紹,接下來我給大家推薦我的一個開源項目,這是一款有較好用戶體驗的開源玩Android客戶端。提供豐富完整的功能,更好的體驗,旨在更好的瀏覽https://www.wanandroid.com/網站內容,更好的在手機上進行學習。項目使用Retrofit2 + RxJava2 + Dagger2 +MVP+RxBus架構,儘量使用Material Design控件進行開發。

項目架構圖

項目地址

https://github.com/maoqitian/MaoWanAndoidClient

總結

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

About me

blog:

mail:

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章