Android架構系列-MVP架構的實際應用

本文簡述了在實際項目中使用MVP架構遇到的問題和相應處理,最終整理出升級版的MVP架構。java

0 Android架構系列文章

該系列文章會不斷更新Android項目開發中一些好的架構和小技巧android

系列一 Android架構系列-基於MVP建立適合本身的架構
系列二 Android架構系列-如何優美的寫Intent
系列三 Android架構系列-開發規範
系列四 Android架構系列-封裝本身的okhttp
系列五 Android架構系列-MVP架構的實際應用git

1 原有的MVP架構

在系列文章的第一篇文章中介紹了使用MVP架構。詳細能夠回看該文章github

MVP的結構以下圖:緩存

MVP
MVP

2 實際項目中應用出現的問題

MVP是一種代碼的分層思想,其實沒有用到任何庫,只是告訴了你如何規整的放置代碼。使各個層次的代碼各司其職,增長易讀性和可測試性。網絡

可是真實開發中發現,MVP是一種模塊中高內聚的模式,Presenter層接管了Activity中的邏輯實現。相應出現瞭如下幾個問題:架構

2.1 Presenter生命週期的問題

Presnter層和View層是一一對應的,因此Presnter層和View層生命週期是一致的。異步

可是如今全部邏輯寫在Presenter層中,若是其餘地方須要調用就只能經過靜態方法調用,不能再次new 一個 Presenter實例ide

2.2 跨模塊調用

實際開發中常常會有在B模塊調用A模塊的部分邏輯。post

好比發帖時要判斷用戶是否登陸,而且獲取當前登陸用戶信息。即在發帖模塊要獲取用戶模塊的數據和邏輯。

若是邏輯寫在Presenter中,則其餘模塊只能直接讀取當前用戶緩存,而後在本身模塊解析。仍是增長了模塊間的耦合。

3 優化的MVP分層

在這裏將Model層命名爲Interactor。咱們將每一個模塊內部的原子邏輯(一個功能而不是一系列邏輯功能)都寫在interactor中,Presenter層只負責接收view事件,調用interactor功能,再回饋view。

在此,一個Presenter能夠持有多個模塊的Interactor,這樣就能夠訪問相應功能邏輯和數據。而且不須要在本身模塊對其餘模塊數據進行解析處理。

new MVP
new MVP

該優化後的分層和普通的MVP最大的區別在於,將Presenter層解放出來,裏面再也不放具體邏輯,直接調用邏輯。

分析各個層:

3.1 View層

  1. 只持有和本身一一對應的Presenter實例,經過實現接口方式調用
  2. 負責頁面的控件初始化, 刷新顯示頁面, 監聽元素事件
  3. 不該該出現狀態, 邏輯等代碼(除非只跟頁面相關的很小的邏輯,好比一個字段標識密碼是否可見)

3.2 Presenter層

  1. 持有和本身一一對應的View實例,能夠持有多個模塊的Interactor層。經過實現接口方式調用。
  2. 做爲View和Interactor層的Glue層, 接收view操做, 調用模塊中方法, 返回數據給view。

3.3 Interactor層

  1. 本模塊中原子性邏輯封裝,非一個系列的邏輯,這樣保證其餘地方能夠方便的調用。
  2. Interactor層中不該該出現其餘模塊的引用
  3. Interactor層的返回。若是是同步直接返回數據, 若是是異步在contract.interactor中定義callback。

4 一個代碼示例

下面簡述一個Sample 登陸代碼

LoginContract層:

public interface LoginContract {

    interface View extends BaseView {
        /** * 跳轉Home */
        void goHome();
    }

    interface Presenter extends BasePresenter {
        /** * login * @param phone * @param password */
        void onLogin(String phone, String password);
    }

    interface Interactor {
        /** * do login * @param phone * @param password * @param callback */
        void doLogin(String phone, String password, LoginCallback callback);
        interface LoginCallback {
            void onSuccess(UserInfo user_info);
            void onFailure(String msg);
        }

        /** * 是否登陸 * @return */
        boolean isLogin();

        /** * 獲取當前登陸用戶 * @return */
        UserInfo getLoginUser();
    }
}複製代碼

LoginActivity:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);
        ButterKnife.bind(this);

        mPresenter = new LoginPresenter(this);
    }

    @OnClick(R.id.btnLogin)
    public void onLogin() {
        mPresenter.onLogin(editPhone.getText().toString(), editPassword.getText().toString());
    }

    @OnClick(R.id.txtRegister)
    public void goRegister() {
        ToastUtils.showShort(GlobalApp.getInstance().getContext(), "goRegister");
    }

    @OnClick(R.id.txtForgetPwd)
    public void goForgetPwd() {
        ToastUtils.showShort(GlobalApp.getInstance().getContext(), "goForgetPwd");
    }

    @Override
    public void showToast(String msg) {
        ToastUtils.showShort(GlobalApp.getInstance().getContext(), msg);
    }

    @Override
    public void goHome() {
        //跳轉Home頁面
        ToastUtils.showShort(GlobalApp.getInstance().getContext(), "登陸成功, 跳轉Home頁面");

        Intent intent = new Intent(this, HomeActivity.class);
        startActivity(intent);
        finish();
    }複製代碼

LoginPresnter:

public class LoginPresenter implements LoginContract.Presenter {

    private LoginContract.View mView;
    private LoginContract.Interactor mInteractor;

    public LoginPresenter(LoginContract.View view) {
        mView = view;
        mInteractor = new LoginInteractor();
    }

    @Override
    public void start() {

    }

    @Override
    public void onLogin(String phone, String password) {
        if(StringUtils.isEmpty(phone)) {
            mView.showToast("Empty phone");
            return;
        }

        if(StringUtils.isEmpty(password)) {
            mView.showToast("Empty password");
            return;
        }

        mInteractor.doLogin(phone, password, new LoginContract.Interactor.LoginCallback() {
            @Override
            public void onSuccess(UserInfo user_info) {
                mView.goHome();
            }

            @Override
            public void onFailure(String msg) {
                mView.showToast(msg);
            }
        });
    }
}複製代碼

LoginInteractor:

public class LoginInteractor implements LoginContract.Interactor {

    private MyOkHttp mApi;
    private ACache mCache;

    //緩存key
    private final String CACHE_KEY_USERINFO = "CACHE_KEY_USERINFO";

    public LoginInteractor() {
        mApi = MyOkHttp.get();
        mCache = ACache.get(GlobalApp.getInstance().getContext());
    }

    @Override
    public void doLogin(String phone, String password, final LoginCallback callback) {
        //模擬異步網絡請求登陸

        Handler handler = new Handler();

        handler.postDelayed(new Runnable() {
            @Override
            public void run() {
                UserInfo userInfo = new UserInfo();
                userInfo.uid = "1212121";
                userInfo.userName = "tsy12321";
                userInfo.token = "wqw13w12312wsqw12";

                //存入緩存
                mCache.put(CACHE_KEY_USERINFO, userInfo);

                callback.onSuccess(userInfo);
            }
        }, 2000);
    }

    @Override
    public boolean isLogin() {
        UserInfo userInfo = (UserInfo) mCache.getAsObject(CACHE_KEY_USERINFO);
        if(!StringUtils.isEmpty(userInfo.uid) && !StringUtils.isEmpty(userInfo.token)) {
            return true;
        }
        return false;
    }

    @Override
    public UserInfo getLoginUser() {
        return (UserInfo) mCache.getAsObject(CACHE_KEY_USERINFO);
    }
}複製代碼

由以上示例能夠看出,具體的邏輯都放在了Interactor層。下面展現其餘模塊如何調用Login模塊的邏輯或者數據。

假如Home頁面點擊發帖,須要判斷當前登陸狀態。則在HomePresenter中同時持有LoginInteractor的實例。

public class HomePresenter implements HomeContract.Presenter {

    private HomeContract.View mView;
    private HomeContract.Interactor mInteractor;
    private LoginContract.Interactor mLoginInteractor;

    public HomePresenter(HomeContract.View view) {
        mView = view;
        mInteractor = new HomeInteractor();
        mLoginInteractor = new LoginInteractor();
    }

    @Override
    public void start() {

    }

    @Override
    public void onPost() {
        //判斷用戶有沒有登陸
        if(!mLoginInteractor.isLogin()) {
            // 跳轉登陸
            // TODO: 16/8/30
            return;
        }

        //跳轉發帖頁面
        // TODO: 16/8/30
    }
}複製代碼

具體的代碼在Github項目:BaseAndroidProject

5 結尾

不管是MVP仍是什麼架構,最終的目的都是寫出易讀性和測試性強的代碼。因此不要對於架構鑽牛角尖,過分設計不可取。在實際開發中架構天然會跟着升級。謹記!均衡合理!!

更多文章關注個人公衆號

個人公衆號
個人公衆號
相關文章
相關標籤/搜索