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 */