Android 基本架構之MVP分析與實踐

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

MVC MVP 架構對比圖

mvc

mvc

mvp

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控件進行開發。

項目架構圖

MVP-WanAndroid-Architecture

項目地址

github.com/maoqitian/M…git

總結

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

About me

blog:

mail:

相關文章
相關標籤/搜索