Android框架模式MVC和MVP,之前已经是有相关总结,但是在开发中用得很少,也没怎么注意,很多代码都是随意写的,但是现在开发要求按照MVP的设计模式,所有我想对MVP框架设计做一个总结,并设计一个Demo程序帮助大家更好的理解MVP,理顺MVP的设计思路,重要的是学会使用MVP。
这里我也是不想讲什么MVC模式跟MVP模式的比较这一方面的知识了,现在一旦使用MVP就很少人使用MVC模式了。
以前一个关于MVP和MVC框架模式相关知识的总结:
http://blog.csdn.net/wenzhi20102321/article/details/53302453
MVP是模型(Model)、视图(View)、主持人(Presenter)的缩写,分别代表项目中3个不同的模块。
MPV 是从经典的MVC模式演变过来的,其基本思路都是相通的。其中M是model模型,提供业务数据;P和MVC中的C担当的角色相似,是Presenter主持人,进行逻辑处理。V是View视图,显示页面操作。
在MVP中View并不直接使用Model,它们之间的通信是通过Presenter 来进行的,所有的交互都发生在Presenter这个类内部,在代码中可以发现Presenter类既有View对象,又有Model对象,但是在Activity中是没有Model对象的,是使用Presenter对象来调用Model对象;Activity是要有View对象和Presenter对象的,Presenter对象怎么来呢,这个是要在构造方法中去创建。View对象怎么来呢,其实是Activity实现这个View,这个Activity就相对于这个View对象,并且需要重写View接口的方法!而且还有一个重要的就是要用这个Presenter来绑定Activity(其实是为了绑定View接口的方法)。这样在Presenter用model处理完数据后,Presenter就会调用View接口的方法,实现页面的操作。
上面这一段话也是MVP框架模式的重点理论,需要理解透彻。你也可以从我的代码中细细的体会,一定要搞清楚它们的关系!
如下图所示,View与Model并不直接交互,而是使用Presenter作为View与Model之间的桥梁。其中Presenter中同时持有Viwe层以及Model层的Interface的引用,而View层持有Presenter层Interface的引用。当View层某个界面需要展示某些数据的时候,首先会调用Presenter层的某个接口,然后Presenter层会调用Model层请求数据,当Model层数据加载成功之后会调用Presenter层的回调方法通知Presenter层数据加载完毕,最后Presenter层再调用View层的接口将加载后的数据展示给用户。这就是MVP模式的整个核心过程。
这样分层的好处就是大大减少了Model与View层之间的耦合度。一方面可以使得View层和Model层单独开发与测试,互不依赖。另一方面Model层可以封装复用,可以极大的减少代码量。当然,MVP还有其他的一些优点,这里不再赘述。
如图所示:
虽然MVP是针对页面的逻辑,但是mvp放到activity包名下就显得很难看。
其实是这样的:MVP中的View的包下放的并不是activity这样的视图类,而是activity视图类的操作,比如这个登录的activity下,会有获得用户的账号,密码的需要,也有点击登录的需要等等,这就需要定义这几个操作的方法,其实这里view包下定义的是接口类,里面的方法都是抽象方法,定义这个页面需要的操作,具体页面内容的实现还是要在Activity中来!
设计数据处理的逻辑,这里一般还设计一个内部接口类,让presenter对象可以设置监听并得到数据。因为好多数据处理都是需要子线程的,不能马上就返回数据。
设计视图中需要的逻辑,都是接口方法,该类也是接口类。这个接口类也是需要固定的Activity页面来实现,并重写里面的方法,在方法里面写入页面的操作,这些重写方法方法能否得到回调都是Presenter对象来控制的,一般都是数据或逻辑处理完后再判断相应的方法回调。
设计activity和model的交互,既要有View对象,也要有model对象。
Activity中把数据相关的逻辑操作都扔给了Presenter去做,View只负责处理与用户界面操作。而Presenter调用Model处理完数据之后,再通过View接口方法更新View显示的信息。
下面是设计一个登录页面的程序,向大家详细介绍设计过程。
界面如下:
点击登录并验证成功后跳转到用户界面。
代码:
/**
* mvp中的View的定义
* 用户登录界面用户的操作行为的定义
*/
public interface LoginView {
String getAccount();//获取用户的账号,返回账号
String getPassword();//获取用户的莫玛,返回密码
void loginSuccess(User user);//登录的实现,需要传入用户对象
void showNetworkError();//显示网络异常
void showVerifyFailed();//信息验证失败,账号或密码有误
}
代码:
/**
* MVP中的model数据处理类
* 这里处理登录时的数据
*/
public class LoginModel {
/**
* 处理登录业务并返回结果
*/
public void login(String name,String password,OnLoginResultListener onLoginResultListener){
//一般登录都是请求服务器,验证
//这里就简单一点,大家别介意
if ("liwen".equals(name)&&"123456".equals(password)){
onLoginResultListener.loginSuccess(new User(name,password));//登录成功,给他返回用户对象
}else {
onLoginResultListener.loginFailure();//登录失败
}
}
//回调接口
public interface OnLoginResultListener {
void loginSuccess(User user);//登录成功后回调的方法,返回User对象
void loginFailure();//登录失败后回掉的方法
}
/**
* mvp中Presenter中的设计
* 也是比较难,需要重点理解的一个
* presenter是主持人的意思,view和model的中间者
* 需要同时要有View的对象和Model的对象!一般做法是:在构造方法中创建model对象,并创建一个方法绑定View接口
* 这里可以发现数据处理后或者逻辑判断完后都是给mvp中的View对象来做操作的!
*/
public class LoginPresenter {
/**
* 登录业务实现者,数据处理的操作者
*/
private LoginModel mLoginModel;
/**
* 在构造方法中实例化model对象
*/
public LoginPresenter() {
mLoginModel = new LoginModel();
}
//视图接口对象
private LoginView mLoginView;
/**
* 绑定View 的方法
*
* @param loginView
*/
public void bind(MyActivity loginView) {
mLoginView = loginView;
}
/**
* 登录业务
*/
public void login() {
String account = mLoginView.getAccount();
String password = mLoginView.getPassword();
Log.e("TAG", "account:" + account + ",password" + password);
if (checkParameter(account, password)) {
doSomePrepare();
//登录 ,需要处理数据,所有要在model中执行
mLoginModel.login(account, password, new LoginModel.OnLoginResultListener() {
//登录成功的回调方法
@Override
public void loginSuccess(User user) {
mLoginView.loginSuccess(user); //在给视图页面返回User对象
}
//登录失败的回调方法
@Override
public void loginFailure() {
mLoginView.showVerifyFailed();//在给视图页面返回验证失败的结果
}
});
}
}
/**
* 做一些准备
*/
private void doSomePrepare() {
//这里可以设置按钮不可点击!否则一直点击登录也是不好
}
/**
* 检测参数是否是否为空~~~
*
* @param account
* @param password
* @return
*/
private boolean checkParameter(String account, String password) {
if (TextUtils.isEmpty(account) | TextUtils.isEmpty(password)) {
mLoginView.showVerifyFailed();//提示错误
return false;
} else if (!checkNetwork()) {
mLoginView.showNetworkError();//提示网络错误
return false;
}
return true;
}
/**
* 检测网络是否可用
*/
public boolean checkNetwork() {
return true;//先显示可以联网,实际中要用代码判断
}
}
上面有些方法是可以不写的,比如检查网络或做一些准备,但是实际程序中,是要做很多判断的,这里就随便啰嗦几句。
/**
* 登录程序示例的Activity
* 这里需要创建presenter对象,presenter对象中是有view对象和model对象的!
*/
public class MyActivity extends Activity implements LoginView, View.OnClickListener {
private EditText et_name;
private EditText et_password;
private Button btn_logon;
private LoginPresenter loginPresenter;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initView();
}
/**
* 初始化数据
*/
private void initView() {
et_name = (EditText) findViewById(R.id.et_name);
et_password = (EditText) findViewById(R.id.et_password);
btn_logon = (Button) findViewById(R.id.btn_login);
btn_logon.setOnClickListener(this);
loginPresenter = new LoginPresenter();
loginPresenter.bind(this);//绑定View和Presenter,因为这个Activity已经实现了接口,已经包含了View对象
}
/**
* 登录按钮的监听方法
* 这里要做后台数据的处理,需要用到Presenter
*/
@Override
public void onClick(View v) {
loginPresenter.login();
}
/**
* 下面五个方法都是实现LoginView后要是实现的方法
*/
@Override
public String getAccount() {
return et_name.getText().toString();
}
@Override
public String getPassword() {
return et_password.getText().toString();
}
@Override
public void loginSuccess(User user) {
//登录成功后,一般是实现页面的跳转
Toast.makeText(this, "登录成功", Toast.LENGTH_SHORT).show();
}
@Override
public void showNetworkError() {
Toast.makeText(this, "当前网络不可用", Toast.LENGTH_SHORT).show();
}
@Override
public void showVerifyFailed() {
Toast.makeText(this, "输入的用户名或密码有误", Toast.LENGTH_SHORT).show();
}
}
上面可以看到在初始化时就创建loginPresenter对象,而loginPresenter的构造方法中也是创建了loginModel对象,Activity实现了LoginView,就相当于有了LoginView对象,这是只需要用loginPresenter绑定LoginView,这时MVP的model、view、presenter三者都已经实例化,并关联在一起了。
上面语句loginPresenter绑定LoginView的是:loginPresenter.bind(this);绑定Avtivity,也是绑定View!loginPresenter中调用LoginView的方法都是在Activity中执行的。
程序运行后的显示:
下面是源码的下载地址:
http://download.csdn.net/detail/wenzhi20102321/9803842
其实上面已经是一个MVP框架模式的标准程序,但是算是最简单哪种,一般的程序还要做更多的判断。比如,判断账号,密码为空等等。
在实际使用MVP模式中,你不只是要创建一个View、Presenter或Model,比较好的做法就是创建三个Base类(BaseView是接口),让每一个View、Pressenter或Model去继承这个Base类,而这个Base类做一些共同的操作,比如:BasePresenter就可以写绑定View的方法,因为每一个Presenter都是必须要绑定View的;而BaseView可以做一些每个页面都需要的方法:显示动画,隐藏动画,显示网络异常的方法等等。总之根据程序的实际需求来设计就好。
也有人会想问使用MVP是不是很麻烦,如果使用MVP不是每个页面多要设计model、presenter和view,这要做多少工作!
其实使用MVP就是要让程序降低耦合度,让逻辑更加清楚;也不是所有的Activity页面都是要使用MVP,一般的涉及到较多数据处理,比如登录界面、注册界面是需要MVP框架模式的,而那些简单页面,比如只是显示简单数据或试图或显示某一个控件的Activity是不需要使用MVP来完成的,按照正常逻辑设计就可以了。
MVP到这里介绍完了,我想很多人应该是可以理解的吧,不理解的话可以下载源码慢慢体会下,同时写个注册页面实践一些,就会有清楚的思路了。也可以发表留言讨论。
共勉:活在当下