菜雞的MVP架構漫談
相信大家在網上看過關於MVP架構的部落格數不勝數,至於MVP到底是什麼,也不需要我再從百度百科複製一遍了,通俗的說MVP就是解決Model和View的耦合,沒有使用架構的程式碼就是一個Activity裡處理了UI邏輯、網路請求等等,有的時候一個Activity五六百行,我曾經看過同事的一個介面卡上千行,因為業務邏輯過於複雜,說了這麼多,MVP到底有什麼作用呢?我們非用不可嗎,MVP可以把"功能"抽離出來,因為一個專案中會出現很多頁面的某個功能是相似的,這就代表我們不用架構的話,重複的程式碼會大量的出現在我們的專案中,這不符合單一原則,最後引用一下大佬的總結,“mvp最關鍵的點就是把Activity中的ui邏輯抽象成view介面,把業務邏輯抽象成presenter介面,model還是資料模型。架構是對客觀不足的妥協,規範是對主觀不足的妥協。”
public interface IBaseView { void showNetError(); void showDataError(); void hideLoading(); void showLoading(); void showMsg(String msg); Activity getMyContext(); }
基類View
public interface IBasePresenter<T> { void attachView(T baseView); void detachView(); boolean isViewAttached(); }
基類Presenter
/** * Presenter 基類 * @param <T> */ public abstract class BasePresenter<T extends IBaseView> implements IBasePresenter<T> { private T view; @Override public void attachView(T baseView) { this.view = baseView; initView(); initModel(); } abstract void initView(); abstract void initModel(); @Override public void detachView() { view = null; } @Override public boolean isViewAttached() { return view != null; } public T getView() { return view; } }
抽象基類Presenter
/** * Model 回撥動作 * @param <T> */ public interface OnCallBackModel<T> { /** * 請求資料成功時回撥動作 * @param datas */ void onSuccess(T datas); /** * 資料請求成功,由後臺返回的code碼非成功碼呼叫動作 * @param msg */ void onFailure(String msg); /** * 網路異常 */ void onNetError(); /** * 資料異常 */ void onDataError(); /** * 網路請求資料完畢後的執行動作,比如隱藏載入動畫 */ void onComplete(); /** * 網路請求資料之前,比如開啟載入動畫 */ void onBefore(); }
Model的回撥介面
以上我自己結合網上部落格上的那些MVP部落格總結修改的,也是自己在專案中用的,架構比較簡單、簡陋,但是對於小專案也夠用了,也沒打算用MVP的開源框架,全自己手寫,這裡解釋下為什麼有抽象基類Presenter,因為所有的Presenter都要判斷View是否貼上、新增、移除View,這樣提取共性比較合適。
在實際的開發過程中,我們可能遇到這種情況,就是一個Activity中需要請求網路判斷是否需要更新當前版本和更新使用者資訊資料,然後另一個Activity也包含了更新使用者資料的功能,這樣我們就有必要為這個單一的功能抽象出來,那我們怎麼在Presenter中去排程呢?
public class AppUpdatePresenter extends BasePresenter<IAppUpdateView> implements IAppUpdatePresenter, IUpdateUserInfoPresenter { private IAppUpdateModel model; private IUpdateUserInfoModel userInfoModel; private IAppUpdateView view; @Override public void appUpdate(final String version, String type) { model.getAppUpdate(version, type, new OnCallBackModel<AppVersionBean>() { @Override public void onSuccess(AppVersionBean datas) { if(isViewAttached()){ view.setAppUpdate(datas); } } @Override public void onFailure(String msg) { if(isViewAttached()){ view.showMsg(msg); } } @Override public void onNetError() { } @Override public void onDataError() { } @Override public void onComplete() { } @Override public void onBefore() { } }); } @Override public void updateUserInfo(String phone) { userInfoModel.getUserInfo(phone); } @Override void initView() { view = getView(); } @Override void initModel() { model = new AppUpdateModel(); userInfoModel = new UpdateUserInfoModel(); } }
最後再貼上Model程式碼
public class AppUpdateModel implements IAppUpdateModel { @Override public void getAppUpdate(String version, String type, final OnCallBackModel callBackModel) { Map<String, String> params = new HashMap<>(2); LogUtils.e("當前版本號------>",version); params.put("version", version); params.put("type", type); HttpManager.postHttp(ConstantUtils.APP_VERSION, params, new StringCallback() { @Override public void onError(Call call, Exception e, int id) { } @Override public void onResponse(String response, int id) { if(StringUtils.isJSONType(response)){ AppVersionBean bean = JsonUtils.fromJson(response, AppVersionBean.class); if(null == bean){ callBackModel.onFailure(ConstantUtils.DON_NEED_UPDATE); return; } if(bean.getCode().equals(ConstantUtils.DATA_SUCCESS)){ callBackModel.onSuccess(bean); }else { callBackModel.onFailure(ConstantUtils.DON_NEED_UPDATE); } } } }); } }
public class UpdateUserInfoModel implementsIUpdateUserInfoModel{ @Override public void getUserInfo(String phone) { Map<String, String> params = new HashMap<>(1); params.put("mobile", phone); HttpManager.postHttp(ConstantUtils.CARD_INFO, params, new StringCallback() { @Override public void onError(Call call, Exception e, int id) { } @Override public void onResponse(String response, int id) { if (StringUtils.isJSONType(response)) { CardInfoBean bean = JsonUtils.fromJson(response, CardInfoBean.class); if (null == bean) { return; } if (null != bean.getData() && bean.getCode().equals(ConstantUtils.DATA_SUCCESS)) { User user = AppUtils.getUserData(); CardInfoBean.DataBean datas = bean.getData(); user.setReal_name(datas.getReal_name()); user.setGender(datas.getGender()); user.setPassport_num(datas.getPassport_num()); user.setPassport_status(datas.getPassport_status()); user.setExpire_time(datas.getExpire_time()); user.setId_num(datas.getId_number()); AppUtils.updateUserData(user); } } } }); } }
最開始我的想法是在更新版本的介面上繼承更新使用者資訊的介面,但是後面想想其實可以實現,但是有一個大問題,違背了單一原則,每一個介面僅僅只負責一個功能,如果需要多個功能,那就新建一個介面多繼承所需的功能,切記不可讓介面繼承介面,介面多繼承介面,類多實現介面。