Android中傳統的沒使用架構的,或者使用mvc結構的,activity都承載了太多功能,既當了view層又當了control層
關於mvc本文不多討論了,記錄下工作中的使用mvp結構的例子
mvp結構從下上實際爲m->p-->v,即數據,控制,ui
先說下應用業務場景:
有關於門店shop的業務,有關於訂單order的業務
public class Shop { private String id; private String icon; private String name; private String address; private List<Time> runningTimes; private int bDRunning; private int mTRunning; private int eleRunning; private int wxRunning; private int wmOpen; public int getWxRunning() { return wxRunning; } public void setWxRunning(int wxRunning) { this.wxRunning = wxRunning; } public String getId() { return id; } public void setId(String id) { this.id = id; } public String getIcon() { return icon; } public void setIcon(String icon) { this.icon = icon; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public List<Time> getRunningTimes() { return runningTimes; } public void setRunningTimes(List<Time> runningTimes) { this.runningTimes = runningTimes; } public int getbDRunning() { return bDRunning; } public void setbDRunning(int bDRunning) { this.bDRunning = bDRunning; } public int getmTRunning() { return mTRunning; } public void setmTRunning(int mTRunning) { this.mTRunning = mTRunning; } public int getEleRunning() { return eleRunning; } public void setEleRunning(int eleRunning) { this.eleRunning = eleRunning; } public int getTRunning() { return mTRunning; } public void setTRunning(int TRunning) { mTRunning = TRunning; } public int getWmOpen() { return wmOpen; } public void setWmOpen(int wmOpen) { this.wmOpen = wmOpen; } @Override public String toString() { return "Shop{" + "id='" + id + '\'' + ", icon='" + icon + '\'' + ", name='" + name + '\'' + ", address='" + address + '\'' + ", runningTimes=" + runningTimes + ", bDRunning=" + bDRunning + ", mTRunning=" + mTRunning + ", eleRunning=" + eleRunning + ", wmOpen=" + wmOpen + '}'; } }門店無非就是id,門店名,門店圖片,地址等等,只關注最簡單基本的數據就可以了
order也是id,支付時間,支付的錢,以及訂單的各個狀態(已支付,未支付,已確認,已關閉)
既然有兩大業務線,這裏以門店爲例來說明,門店的業務需求接口有(只關注與服務器交互有關的):
1 獲取門店數據
2 改變門店營業狀態
3 設置店鋪營業時間
4 獲取門店日結賬單
5 等等
以下提供的代碼看起來多的話,只聚焦一個業務需求,獲取門店的日結賬單,將會以這個小業務需求爲例子
顯然這是M層,我們可以定義一個接口IShopModel來代表門店這個業務需求
public interface IShopModel { interface OnGetShopLisener { void onSuccess(Shop shop); } interface ShopTimeGettingListener { void onGetTime(List<Time> times); } interface ShopTimeSettingListener { void onSetting(boolean b); } interface ChangeRunningStatusListener { void onSuccess(); void onFailed(); } interface DayBillCallback { void onGetDayBill(DayBillResponse response); } interface NoticePubListener { void onPubSuccess(); void onPubFailed(); } interface NoticeGettingListener { void onGettingNotice(String notice); } /** * 獲取門店數據 * * @param context * @param listener */ VPayUIRequestV2 getShop(Context context,boolean isShow,OnGetShopLisener listener); /** * 改變門店營業狀態 * * @param context * @param platform * @param actionType * @param listener */ VPayUIRequestV2 changeRunningStatus(Context context, int platform, int actionType, ChangeRunningStatusListener listener); /** * 獲取店鋪營業時間 * * @param context * @param listener */ VPayUIRequestV2 getShopRunningTime(Context context, ShopTimeGettingListener listener); /** * 設置店鋪營業時間 * * @param context * @param listener */ VPayUIRequestV2 setShopRunningTime(Context context, List<Time> mShopTimeList, ShopTimeSettingListener listener); /** * 獲取門店日結賬單 * * @param context * @param date * @param callback */ VPayUIRequestV2 getShopDayBill(Context context, long date, DayBillCallback callback); /** * 發佈公告,由於該功能已屏蔽,暫不修改了 * * @param context * @param notice * @param listener */ void publishNOtice(Context context, String notice, NoticePubListener listener); /** * 獲取公告 * * @param context * @param listener */ void getNotice(Context context, NoticeGettingListener listener); }
然後我們再定義一個門店的實現類ShopModleImpl implements IshopModel
該類會對所有的業務進行真正的實現
@Override public VPayUIRequestV2 getShopDayBill(final Context context, long date, final DayBillCallback callback) { DayBillRequest body = new DayBillRequest(); body.setDate(date); String url =""; final VRequest<?> requestV2 = new VRequest<DayBillResponse>(url, body, context, false) { @Override public boolean onResponse(DayBillResponse response) { if (response.getCode() == HttpUtils.CODE_SUCCESS) { callback.onGetDayBill(response); return true; } else { Toast.makeText(context, response.getReason(), Toast.LENGTH_SHORT).show(); return false; } } }; requestV2.setShouldCache(false); requestV2.send(); return requestV2; }
上面是對獲取門店日結賬單的實現例子
上面有一行代碼
callback.onGetDayBill(response);
這是對從服務器獲取數據對象後的回調,按照以往後者mvc結構,一般會回調到Activity中,但在mvc中,我們不直接給activity,而是給presenter層
同理,定義一個代表門店業務的Presenter
/** * 關於門店業務的總接口 * 爲減少接口爆發,就實現IShopPresenter這一個總接口,只複寫與其業務有關的方法 */ public interface IShopPresenter { /** * 獲取門店數據 */ VPayUIRequestV2 getShop(Context context,boolean isShow); /** * 展示更改營業狀態的dialog * * @param platform,要更改的平臺,例如,0 美團,1百度,2餓了麼 * * 注意,這是錯誤的mvp使用,與model無關的控制不必交給presenter, * 否則會導致類似V->P->V->P->V這種無用功 */ // void showDialogChangeStatus(int platform); /** * 改變某個平臺的營業狀態 * * @param platform 平臺 * @param actionType 0 關閉,1 營業 */ VPayUIRequestV2 changeRunningStatus(Context context, int platform, int actionType); /** * 獲取店鋪營業時間 * * @param context */ VPayUIRequestV2 getShopRunningTime(Context context); /** * 設置店鋪營業時間 * * @param context */ VPayUIRequestV2 setShopRunningTime(Context context, List<Time> mShopTimeList); VPayUIRequestV2 getShopDayBill(Context context, long date); }
對於上面的接口,如果我們每個具體的presenter(例如獲取門店數據presenter,設置營業時間presenter,獲取門店日結賬單presenter)直接實現它,會每個present中有大量的空實現方法,因此這裏採用適配器模式,用一個基類BaseShopPresenter對其空實現,子類集成基類,複寫與自身相關的業務方法
public class BaseShopPresenter implements IShopPresenter { @Override public VPayUIRequestV2 getShop(Context context,boolean isShow) { return null; } @Override public VPayUIRequestV2 changeRunningStatus(Context context, int platform, int actionType) { return null; } @Override public VPayUIRequestV2 getShopRunningTime(Context context) { return null; } @Override public VPayUIRequestV2 setShopRunningTime(Context context, List<Time> mShopTimeList) { return null; } @Override public VPayUIRequestV2 getShopDayBill(Context context, long date) { return null; } }
這裏ShopDayDillPresenter只複寫與其相關的業務getShopDayBill()
public class ShopDayBillPresenter extends BaseShopPresenter { public interface IShopDayBillView { void fillData(DayBillResponse response); } private IShopDayBillView mShopDayBillView; private IShopModel mShopModel; public ShopDayBillPresenter(IShopDayBillView shopDayBillView) { mShopDayBillView = shopDayBillView; mShopModel = new ShopModleImpl(); } @Override public VPayUIRequestV2 getShopDayBill(Context context, long date) { return mShopModel.getShopDayBill(context, date, new IShopModel.DayBillCallback() { @Override public void onGetDayBill(DayBillResponse response) { mShopDayBillView.fillData(response); } }); } }
關鍵一行的代碼
mShopDayBillView.fillData(response);
可以看到由p控制view,填充數據
然後再看view層,怎麼進行調用
在DayBillActivity中,我們只需在需要數據的時候
/** * 獲取數據 * * @param date 指定獲取時間 */ public void initData(long date) { IShopPresenter iShopPresenter = new ShopDayBillPresenter(this); mRequestV2 = iShopPresenter.getShopDayBill(mContext, date); }
就可以了,當數據返回回來的時候,會自動進行填充
@Override public void fillData(DayBillResponse response) { //you can bind your data to view }
這樣從一個業務請求開始,數據返回,數據填充,用mvp就表示完了.
在寫mvp時,本人也看了網上很多其他的寫法
最終用這個結尾吧
/** * 本項目於2016.04.15從mvc改爲mvp結構 * 在該結構中,modle層與mvc時一樣保持不變,增加presenter層,其負責具體的數據業務控制邏輯, * 注意,只與數據有關的才交給其處理 * view層每一個具體的元素只負責其ui的初始化initView(Paramter pra...), * 數據的抽象初始化initData(Parameter pra...),以及需要model層幫助的交互 * mvp結構中的要注意的地方: * 交互事件不一定要傳給Presenter, * 這裏有個原則,就是如果這個事件需要Model層的幫助, * 那事件必須傳給Presenter, * 否則請不要傳給Presenter,讓View自己處理, * 這樣纔不會導致類似V->P->V->P->V這種無用功 * 特別是網上抄來抄去的showDialog,hideDialog * public interface ISplashView { * void showProcessBar(); * void hideProcessBar(); * void showNetError(); * void startNextActivity(); * } * 這個簡單例子中可以看到View的接口方法就如此之多, * 按這種寫法在實際工程中View的接口必定會膨脹開來。 * 一個接口中方法過多也必然違背了單一接口原則。 * 以後更換View實現的過程是非常痛苦的, * 或者說幾乎更換不了View實現,那分層的意義就失去了。 * 之所以會導致這種問題是因爲你的Presenter告訴View怎麼去渲染, * 而不是告訴View“直接”用什麼去渲染。更通俗地說, * Presenter應該直接給View的加工後的數據。 * 而View自己負責要怎麼去渲染。更多詳細可以查看: * http://blog.csdn.net/duo2005duo/article/details/50594757 */