Android MVP Contract分析

相關文章:

Android MVP - Contract

谷歌官方MVP Contract分析

View: 只處理UI及頁面效果的細節,向Presenter暴露更新UI的方法;並且持有Presenter的引用,通過Presenter對其暴露的方法進行一些初始化頁面以及業務提交等動作,但不關注動作的具體實現。

Presenter: 只關注業務邏輯的細節,持有View的引用,通過調用View層向其暴露的方法去更新UI (這裏的View引用不是具體某個控件的引用,我們也不能讓Presenter持有某一控件的引用);並且也持有一個或者多個model的引用(在於你想將Presenter,也就是業務邏輯拆分的程度,避免Presenter也像MVC中Controller一樣被撐爆),可以使用model,通過對數據庫或者網絡的訪問從而拿到數據,調用View暴露的方法去刷新UI。

Model:向Presenter暴露獲取、存儲、提交數據等方法,具體實現細節Presenter不關注;Model通過Callback 將數據返回給Presenter。

Ok,按照之前規定的原則:

  • View 向 Presenter 暴露更新UI的方法,於是我們有了 IView
  • Presenter 向 View 暴露執行一些特定業務方法,比如初始化頁面,提交等。

於是,就產生了第一種MVP模式:

 

看到以上類圖,可能會有人有疑問:

  1. 在IView中爲什麼我不直接寫 updateView(Response r),這樣不是寫的接口方法更少嗎? 這個問題問的好!因爲我曾經也有過這樣的問題,試想一下,如果我們把Model中的Response直接存在於Activity(View層)中,那當我們更改Response的時候,會導致View層也需要進行相應的變動,還能做到View和Model的完全解耦嗎?所以View 向 Presenter暴露的方法參數一定要符合兩點: (1). 基本數據類型 (2). 公共數據類型
  2. 爲什麼要寫IPresenter,讓Activity直接引用 XXXPresenter不就好了? 確實,這樣做從功能上來說也可以,但是,爲了更嚴格一些,讓Presenter向View暴露那些View關注的方法,這樣開發View的同學就會一下子明白自己只需要關注哪些方法就夠了;同理,Presenter持有IView的引用而不是Activity的也是這個原因。

按照以上模式,我們用僞代碼來實現一下第一種MVP模式(以下代碼不嚴謹,只表達意思):

Presenter:

interface IPresenter {
        void initData();

        void commit();
    }

    class XXXPresenter implements IPresenter {
        private IView mView;
        private Model mModel;
        private Callback mCallback = new Callback() {
            public void onSuccess(Response r) {
                mView.cancelLoading();
                if (r.url == 'initDataUrl') {
                    mView.updateHeader(r.header);
                    mView.updateContent(r.content);
                } else if (r.url == 'commitUrl') {      // 頁面跳轉或者其他什麼的
                }
            }

            public void onError(Error e) {
                mView.showError();
            }
        };

        XXXPresenter(IView view) {
            mView = view;
            mModel = new Model();
        }

        @Override
        public void initData() {
            mView.showLoading();
            mModel.request(xx, xx, mCallback);
        }

        @Override
        public void commit() {
            mView.showLoading();
            mModel.request(xx, xx, mCallback);
        }
    }

View:

interface IView {
        void updateHeader(String header);

        void updateContent(String content);

        void showLoading();

        void cancelLoading();

        void showError(String error);
    }

    public class XXXActivity extends BaseActivity implements IView {
        private IPresenter mPresenter;
        private TextView tvHeader;
        private TextView tvContent;

        public void onCreate(Bundle saveInstanceState) {
            super.onCreate(saveInstanceState);
            setContentView(layout);
            initView();
            initData();
        }

        public void initView() {
            tvHeader = findViewById(..);
            tvContent = findViewById(..);
            tvHeader.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    mPresenter.commit();
                }
            });
        }

        public void initData() {
            mPresenter = new XXXPresenter(this);
            mPresenter.initData();
        }

        @Override
        public void updateHeader(String header) {
            tvHeader.setText(header);
        }

        @Override
        public void updateContent(String content) {
            tvContent.setText(content);
        }
    }

由於 showLoading、cancelLoading 以及 showError都是公用方法,所以你可以把它們放到 BaseActivity中,不用每個Activity都實現一遍。

講完了嗎?Contract在哪呢? 好漢不要急,待我娓娓道來~
以上的MVP確實解決了兩個問題:

  1. Activity過重
  2. View與Model解耦

但是,我覺得還沒有解決好,哪裏還有問題呢:

  1. IView 每次都要聲明 showLoading等公共方法
  2. Presenter中每次請求時都要調用mView的showLoading等公共方法
  3. 能不能把IView和IPresenter定義在同一個文件裏,這樣我就能一眼看出這個頁面的業務是什麼樣子的。

帶着這幾個問題,Google官方的MVP Contract橫空出世:

https://github.com/googlesamples/android-architecture

關注一下工程結構,如下:

如果我們按照模塊劃分包的話,最適合這種模式,我們來看下一個模塊下的組成:
AddEditTaskContract

  • 由於我們再BasePresenter中因爲網絡請求可能會調用到View的showLoading等方法,所以BasePresenter我寫成了抽象類,並且指定一個<? extends BaseView>的泛型;
  • 上圖指定一個Presenter的泛型,目的是爲了:
public interface BaseView<T> {
        void setPresenter(T presenter);
    }

AddEditTaskFragment

AddEditTaskPresenter

而Presenter的實例化完全可以放到Activity中執行,所以並不需要給BaseView指定Presenter的泛型(可能有點繞。。。。。。),在按照之前第一種MVP模式的業務邏輯再上一張優化後的類圖 :

MVP 之 Contract

話不多說,直接貼一段優化後的僞代碼:

//BaseView:
    public interface BaseView {
        public void showLoading();
        public void cancelLoading();
        public void showError();
    } 

    //BaseActivity
    public class BaseActivity extends Activity implements BaseView {    
        public void showLoading() {
                LoadingUtil.showLoading(this);
            }    
        public void cancelLoading() {
                 LoadingUtil.cancelLoading(this);
            }    
        public void showError() {
                 LoadingUtil.showError(this);
            }
    }

    //BasePresenter:
    public abstract class BasePresenter<View extends BaseView> {    
        private View mView;    
        protected Callback mCallback = new Callback() {             
            public void onStart() {
                          mView.showLoading();
               } 
            public void onSuccess(Response r) {
                          mView.cancelLoading();
                          onRequestSuccess(r);
                }             
            public void onError(Error e) {
                         mView.showError();
                         onRequestFailed(e);
                }
               } ;       
            protected abstract void onRequestSuccess(Response r);       
            protected abstract void onRequestFailed(Error e);
    }
    // Contractinterace 
        Contract {    
            interface View extends BaseView {        
                void updateHeader(String header);        
                void updateContent(String content);
            }   
            abstract class Presenter extends BasePresenter<View> {       
                protected Presenter(View view) {           
                    super(view);
                    }       
                protected abstract void initData();       
                protected abstract void commit();
                    
                }
    
        }
// 具體的

XXPresenterclass XXPresenter extends Contract.Presenter {     
            private Model mXXModel;     
            protected XXPresenter(View view) {           
                super(view);
                mXXModel = new Model();
            }    
            protected void initData() {
                 mXXModel.request(xx, xx, mCallback);
            }    
            protected void commit() {
            mXXModel.commit(xx, xx, mCallback);
            }    
            protected void onRequestSuccess(Response r) {          
                if (r.url == 'initDataUrl') {
                   mView.updateHeader(r.header);
                   mView.updateContent(r.content);
                } else if (r.url == 'commitUrl') {              
                // 頁面跳轉或者其他什麼的
                }
             }    
            protected void onRequestFailed(Error e) {         
            // 做一些其他的error邏輯,基類已經調用baseview的showerror
            }
    }
 // XXActivity
    public class XXActivity extends BaseActivity implements Contract.View {       
        private Contract.Presenter mPresenter;       
        private TextView tvHeader;       
        private TextView tvContent;   
    
        public void onCreate(Bundle saveInstanceState) {          
              super.onCreate(saveInstanceState);
              setContentView(layout);
              initView();
              initData();
        } 

        public void initView() {
             tvHeader = findViewById(..);
             tvContent = findViewById(..);
             tvHeader.setOnClickListener(new OnClickListener(){             
             public void onClick(View v) {
                 mPresenter.commit();
             }
             });
        }     
        public void initData() {
         mPresenter = new XXPresenter(this);
         mPresenter.initData();
         }     
         @Override
         public void updateHeader(String header) {
             tvHeader.setText(header);
         }    
         @Override
         public void updateContent(String content) {
             tvContent.setText(content);
         }    
    }

如果是一個View組件而不是Activity或者Fragment,也可以使用,那就需要我們把BaseActivity對於View的公共處理抽成一個BaseViewHelper,這樣儘量多地複用代碼,代碼我就不貼了哈,這也是個題外話~

​​​​

結束語

以上就是MVP的Contract寫法,單看Contract,個人感覺還是比較清晰的,而且再加一部分的代碼複用,我相信會讓你的app更加敏捷地迭代~


 

 

 

 


 

 

 

 

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