【Android 進階】MVP 實戰演練之深入淺出

前言

上一篇文章
【Android 進階】ButterKnife+Retrofit+Picasso+RecycleView 實現一個小案例) 分享了在使使用 RecycleView 的時候如何結合 ButterKnife、Retrofit、Picasso 等框架進行使用。現在來聊聊 MVC 以及 MVP 框架吧。

下面來看看 MVC 的介紹:

MVC簡介

MVC 全名是 Model View Controller,是模型(model)-視圖(view)-控制器(controller)的縮寫,一種軟件設計典範,用一種業務邏輯、數據、界面顯示分離的方法組織代碼,在改進和個性化定製界面及用戶交互的同時,不需要重新編寫業務邏輯。

其中 M 層處理數據,業務邏輯等;V 層處理界面的顯示結果;C 層起到橋樑的作用,來控制 V 層和 M 層通信以此來達到分離視圖顯示和業務邏輯層。

Android 中的 MVC

  • 視圖層(View)

一般採用 XML 文件進行界面的描述,這些 XML 可以理解爲 AndroidApp 的 View。使用的時候可以非常方便的引入。同時便於後期界面的修改。邏輯中與界面對應的 id 不變化則代碼不用修改,大大增強了代碼的可維護性。

  • 控制層(Controller)

Android 的控制層的重任通常落在了衆多的 Activity 的肩上。這句話也就暗含了不要在 Activity 中寫代碼,要通過 Activity 交割 Model 業務邏輯層處理,這樣做的另外一個原因是 Android 中的 Activity 的響應時間是 5s,如果耗時的操作放在這裏,程序就很容易被回收掉。

  • 模型層(Model)

我們針對業務模型,建立的數據結構和相關的類,就可以理解爲 AndroidApp 的 Model,Model 是與 View 無關,而與業務相關的。對數據庫的操作、對網絡等的操作都應該在 Model 裏面處理,當然對業務計算等操作也是必須放在的該層的。就是應用程序中二進制的數據。

MVC MVP 對比

image

通過分析上圖,只需知道 MVC 傳統模式是沒有把 View 和 Model 層隔離開的,MVP 模式則是 View 層和 Model 完全解耦開,通過 Presenter 這個中間人進行傳遞信息。

下面看看 MVP 的介紹:

MVP

image

View:負責繪製 UI 元素、與用戶進行交互(在 Android 中體現爲 Activity)

Model:負責存儲、檢索、操縱數據(有時也實現一個 Model interface 用來降低耦合)

Presenter:作爲 View 與 Model 交互的中間紐帶,處理與用戶交互的負責邏輯。

View interface:需要 View 實現的接口,View 通過 View interface 與 Presenter 進行交互,降低耦合,方便進行單元測試

一句話解釋就是:Presenter 是 View 和 Model 之間的代理。

MVP 優點

  1. 降低耦合度,實現了 Model 和 View 真正的完全分離,可以修改 View 而不影響 Modle
  2. 模塊職責劃分明顯,層次清晰
  3. 隱藏數據
  4. Presenter 可以複用
  5. 利於測試驅動開發
  6. View 可以進行組件化
  7. 代碼靈活性

MVP缺點

  1. Presenter 中除了應用邏輯以外,還有大量的 View->Model,Model->View 的手動同步邏輯,造成 Presenter 比較笨重,維護起來會比較困難。

  2. 由於對視圖的渲染放在了 Presenter 中,所以視圖和 Presenter 的交互會過於頻繁。

  3. 如果 Presenter 過多地渲染了視圖,往往會使得它與特定的視圖的聯繫過於緊密。一旦視圖需要變更,那麼 Presenter 也需要變更了。

  4. 額外的代碼複雜度及學習成本。

代碼實現:

MVP 是一種思想,每個人的理解不一樣,所以每個人的實現都是大同小異的。谷歌推出官方的 MVP Demo,我們可以參考谷歌給出的進行稍微修改一點點,形成自己風格的 mvp 模式。注意,模式的實現是沒有所謂的標準的,只要達到這種解耦效果就可以了。

這裏以 手機助手 的【推薦】欄目舉例說明

模塊解析:

  • Contract 接口:裏面定義 presenter 接口 和 view 接口

  • presenter :負責和 view,module 交互

  • view :基本都是對控件進行更新即可

BaseView

public interface BaseView {

    //聲明公共的一些方法
    void showLodading();//顯示加載進度條
    void dimissLoading();//關閉加載進度條
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

RecommendContract

該類存放兩個接口,View 接口和 Presenter 接口。
以前兩個接口都是分開寫的,現在合起來放在一個接口類裏面,顯得不那麼凌亂了。

接口 View 給具體視圖層實現,譬如本例中的 RecommendFragment

接口 Presenter 給具體的 Presenter 層實現,譬如本例的 RecommendPresenter


public interface RecommendContract {

    //接口與接口之間的繼承是用 extends
    interface View extends BaseView{

        void showResult(List<AppInfo> datas);  //顯示數據
        void showNodata();                     //提示沒數據
        void showError(String msg);            //提示錯誤
    }

    // BasePresenter 暫時爲空,就不列代碼出來了,以後可以增加
    interface  Presenter extends BasePresenter{

        public void requestDatas();//請求數據
    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Model 層

RecommendModel 類裏面調用到的 HttpManager、ApiService 類的代碼就不貼出來了,因爲本例主要講 MVP 模式。

public class RecommendModel {

    // presenter 層調用該方法,執行完畢有回調方法
    public  void getApps(Callback<PageBean<AppInfo>> callback){

        HttpManager manager = new HttpManager();

        ApiService apiService =manager.getRetrofit(manager.getOkHttpClient()).create(ApiService.class);

        apiService.getApps("{'page':0}").enqueue(callback);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Presenter 層

實現 Contract 層接口 RecommendContract.Presenter,實現該接口下的抽象方法。

實現抽象方法:requestDatas(),請求數據

代碼中 引用 model 層的對象RecommendModel mModel,
通過 mModel.getApps() 調用 Model 層的具體方法實現需求。

引用 view 接口實例對象,接口引用指向一個對象 RecommendContract.View mView,
通過 mView.showLodading()、mView.showNodata() 等調用 View 層具體方法實現需求。

public class RecommendPresenter implements RecommendContract.Presenter {

    //引用 model 層的對象
    private RecommendModel mModel;

    //引用 view 接口實例對象,接口引用指向一個對象
    private RecommendContract.View mView;

    //構造方法中傳過來 view 對象
    public RecommendPresenter(RecommendContract.View view){

        this.mView = view;

        mModel = new RecommendModel();
    }


    //實現 RecommendContract.Presenter 接口,重寫接口的抽象方法
    @Override
    public void requestDatas() {

        //調用 實現了RecommendContract.View 接口的  fragment 裏面重寫的 showLodading()方法
        mView.showLodading();

        mModel.getApps(new Callback<PageBean<AppInfo>>() {
            @Override
            public void onResponse(Call<PageBean<AppInfo>> call, Response<PageBean<AppInfo>> response) {

                if(response !=null){

                    mView.showResult(response.body().getDatas());
                }
                else{
                    mView.showNodata();
                }

                mView.dimissLoading();
            }
            @Override
            public void onFailure(Call<PageBean<AppInfo>> call, Throwable t) {
                mView.dimissLoading();
                mView.showError(t.getMessage());
            }
        });
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47

View 層

實現 Contract 層接口 RecommendContract.View,實現該接口下的幾個抽象方法:
showLodading(),
dimissLoading(), showNodata(), showError(), showResult()

代碼中使用 RecommendContract.Presenter mPresenter 接口實例對象,接口引用指向一個對象,
通過 mPresenter.requestDatas() 調用 Presenter 的 requestDatas()方法。
以此達到解耦的目的。

public class RecommendFragment extends Fragment  implements RecommendContract.View {

    @BindView(R.id.recycle_view)
    RecyclerView mRecyclerView;

    private RecomendAppAdatper mAdatper;

    private ProgressDialog mProgressDialog;

    private RecommendContract.Presenter mPresenter;//presenter接口實例對象,接口引用指向一個對象

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {


        View view = inflater.inflate(R.layout.fragment_recomend, container, false);
        ButterKnife.bind(this, view);

        mProgressDialog = new ProgressDialog(getActivity());

        //實例化 Presenter 
        mPresenter = new RecommendPresenter(this);

        initData();
        return view;

    }


    private void  initData(){

        //調用 presenter 去請求數據,實際上,presenter 是指揮 Model 去做實際操作
        mPresenter.requestDatas();

    }


    //數據顯示
    private void initRecycleView(List<AppInfo> datas){

        //爲RecyclerView設置佈局管理器
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));


        //爲RecyclerView設置分割線(這個可以對DividerItemDecoration進行修改,自定義)
        mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL_LIST));

        //動畫
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());


        mAdatper = new RecomendAppAdatper(getActivity(),datas);

        mRecyclerView.setAdapter(mAdatper);

    }



    // 實現 RecommendContract.View  接口後  重寫的抽象方法
    @Override
    public void showResult(List<AppInfo> datas) {
        initRecycleView( datas);
    }

    @Override
    public void showNodata() {

        Toast.makeText(getActivity(),"暫時無數據,請吃完飯再來",Toast.LENGTH_LONG).show();
    }

    @Override
    public void showError(String msg) {
        Toast.makeText(getActivity(),"服務器開小差了:"+msg,Toast.LENGTH_LONG).show();
    }

    @Override
    public void showLodading() {

        mProgressDialog.show();
    }

    @Override
    public void dimissLoading() {

        if(mProgressDialog.isShowing()){
            mProgressDialog.dismiss();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92

總結

通過上面代碼分析可以很清晰的明白 MVP 框架模式是怎麼進行代碼解耦的。下面再次梳理一下 MVP 實現步驟:

  • 定義 Presenter、View 接口(可以向上面例子,放在 Contract 接口裏面):接口裏面定義 presenter 層、view 層的抽象方法。簡單的說就具體實現類所要實現的方法。

  • 具體 Presenter 層實現類實現定義的 Presenter 接口,實現該接口的抽象方法。
    比如 RecommendPresenter 類實現了 RecommendContract.Presenter 接口,實現 requestDatas() 方法。

  • 具體 View 層實現類實現定義的 View 接口,實現該接口下的抽象方法。
    比如 RecommendFragment 類實現了 RecommendContract.View 接口,實現了 dimissLoading(), showNodata(), showError(), showResult() 等方法。

  • RecommendFragment 通過類中的 RecommendPresenter 對象調用 Presenter 層裏面的方法:requestDatas() 方法。在 Presenter 層中通過 Model 對象調用 Model 層裏面的具體方法:getApps(). Model 層的方法執行完後通過回調把結果回調給 Presenter 層,Presenter 層再通過 View 層的接口實例對象,調用相關方法把結果回調到具體實現了該 View 接口的 View 層。




歡迎關注我的微信公衆號:

這裏寫圖片描述

轉自:https://blog.csdn.net/leaf_130/article/details/72579597

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