基于项目需要,特别设计了一套简单的MVP开发框架,用以描述基于REST接口协议的业务数据流向-获取到展示的过程。
基于以上描述,我们先来介绍一下View和Presenter的定义:
1. View模块
View的相关类设计包括一个接口IView,一个基类BaseView。
IView
public interface IView {
public void onActionStart();
public void onActionSuccess();
public void onActionFailure();
}
IView定义了一些抽象方法,用来描述View类型应该具备的基本行为(方法),作为与Presenter进行交互的渠道。这里定义的三个方法描述了开始、成功、失败共三种行为,则具体的展示方式则有其实现类完成。
BaseView
public abstract class BaseView extends Activity implements IView {
@Override
public void onActionStart() {
Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionStart");
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Action starts...", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onActionSuccess() {
Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionSuccess");
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Action success!", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onActionFailure() {
Log.d(Constants.TAG_DEMONCAT, getClass().getSimpleName() + " -> " + "onActionFailure");
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Action failure!", Toast.LENGTH_SHORT).show();
}
});
}
}
BaseView实现了IView的三个通用行为方法,以Toast展示成功失败,以ProgressDialog展示等待状态。更多的样式及行为完全自定义。考虑到APP交互设计过程中,特别的网络请求过程,大致行为相同,所以可以抽象一部分在基类中实现,业务模块子类则不用单独实现。
2. Presenter
Presenter部分也包含一个IPresenter接口,一个BasePresenter基类。
IPresenter
public interface IPresenter {
void destroy();
}
IPresenter接口,定义了通用行为-destroy。
BasePresenter
public abstract class BasePresenter implements IPresenter {
public void destroy() {
// TODO destroy
}
}
BasePresenter实现了IPresenter的destroy方法,同时可以增加自己的通用行为。
我们都知道,View和Presenter的交互是双向的,View通过用户动作向Presenter发起指令,Presenter执行完之后,将结果告知View做进一步的展示。那么View和Presenter是如何完成交互,它们的关系是如何建立的呢?
首先,我们定义一个原则:
这样定义的考虑是,更加明确清晰具体的业务功能。比如,登录功能,则对应一个LoginView和LoginPresenter。基于这个原则,我们可以通过如下方式,将Presenter与View关联起来,
public abstract class BasePresenter implements IPresenter {
protected V mView;
public BasePresenter(V view) {
mView = view;
}
public void destroy() {
mView = null;
}
}
我们对BasePresenter增加范型限制,并且该类型必须是IView的实现类,这样就可以将具体业务的Presenter和View给关联起来。同时,Presenter中包含有一个IView实现类的对象mView,在创建Presenter时传入作为属性,那么在需要告知View该展示何种状态时,则可以通过该View属性,完成消息传递-方法调用。还是以登录作为例子,
public class LoginPresenter extends BasePresenter {
public void login(String username, String password) {
// TODO
}
public interface LoginView extends IView {
public void onLoginSuccess(String token);
public void onLogining();
public void onLoginFailure();
}
}
此时,LoginPresenter声明了范型LoginView,而LoginView是作为内部接口来定义的,并且继承了IView。这么做就是考虑到,具体的展示策略是由Presenter来规范的,那么将具体业务的View定义在该业务的Presenter中,是很合理的。想要具备此业务功能,则实现该View接口。LoginPresenter对外提供了登录功能的对应方法login(), 入餐为用户名和密码;在完成登录后,LoginPresenter则可以通过LoginView对象,来完成消息传递,告知成功、失败。
上面讲到的是从Presenter到View的关联,那么接下来我们来看View到Presenter的关联。
public abstract class BaseView extends Activity implements IView {
private IPresenter[] mAllPresenters = null;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// init all presenter will be used
mAllPresenters = getPresenter();
}
@Override
protected void onDestroy() {
super.onDestroy();
// destroy all presenters when destroy view
if (mAllPresenters != null && mAllPresenters.length > 0) {
for (IPresenter presenter : mAllPresenters) {
presenter.destroy();
}
}
}
}
这里看到,BaseView中定义了Presenter类型的数组对象,该对象保存着当前所需业务对应的所有Presenter对象。并且在onDestroy方法中全部调用destory。可能有人会疑问,上面讲到了一个View-Presenter的一对一关联原则,这里为何是一对多?
其实刚才讲的一对一,是在具体的单个业务功能的角度来看。View的具体业务实现类定义由Presenter的对应业务实现类来完成定义,这样都对应一个具体功能,站在具体功能业务角度,就是一对一的关系。而这里的代码看起来是一对多的关系,是因为用到了数组,这里是站在代码实现的角度。假设这个BaseView的具体实现类需要仅完成一个功能,那么Presenter数组个数为1,那么就是一对一关系;如果要完成多个功能,那么数组个数就为N,这里它已经集成了多个功能业务,那么这个View既是功能A的View,也是功能B的V,这里已经不是站在一个业务功能上来看待这个View了。BaseView是Activity的子类,但不能够完全对等于MVP模式中的View。
public class LoginActivity extends BaseView implements LoginPresenter.LoginView{
private LoginPresenter mPresenter = new LoginPresenter(this);
@Override
protected BasePresenter[] getPresenter() {
return new BasePresenter[]{mPresenter};
}
/*-----------Implements of LoginView-----------*/
@Override
public void onLoginSuccess(String token) {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
Toast.makeText(
getApplicationContext(), "登录成功 token:" + token, Toast.LENGTH_SHORT).show();
}
@Override
public void onLogining() {
mProgressDialog =
ProgressDialog.show(this, null, "登录中...");
}
@Override
public void onLoginFailure() {
if (mProgressDialog != null) {
mProgressDialog.dismiss();
}
Toast.makeText(
getApplicationContext(), "登录失败!", Toast.LENGTH_SHORT).show();
}
}
上面这段代码,则是以登录的Activity为例,按照上述思想,很容易理解。所以,在实现View和Presenter的时候,分别继承BaseView和BasePresenter来完成具体业务功能开发即可。
至此,V和P的关联关系的设计思路已经说明完毕,下一篇将介绍核心-业务数据层M的设计思路及实现。