前言
新年新气象,Blin祝大家在新的一年中事业一帆风顺!
好了,年是过完了,不知道小伙伴们是否也都已经到了工作岗位,开始新的一年的征程了呢。废话不多说,我们这次主要是以聊一聊为
主,简单讲一讲在我们的Android开发中被大家普遍接受的MVP开发,上干货!
简介
对于MVP,全称是Model View Presenter,相比对这东西都有点耳熟能详的感觉有木有,现在很多项目都是用这个开发模式进行公司
或个性项目的开发。其实我在第一次了解MVP的时候,感觉就是东西不错,但是烦。那么MVP到底是什么呢?其实也很简单,就是由我们
以往一直使用的MVC演化而来,如果你做过公司的大型项目,想必你也无时无刻不在抱怨,一个项目主页面的Activity少
则数千行,多则上万行。当然此篇博文更多的是作为一篇日记一样以及本人的一些想法,如果有什么问题的话也请大牛们指正,一起相互学习。
和MVC的区别
这一点的话必须要搞清楚,为什么会有MVP,归根结底就是为了弥补一些MVC的缺点。
它们的基本思想肯定是有相通的地方:Controller/Presenter负责逻辑的处理,Model提供数据,View负责显示。作为一种新的模
式,MVP与MVC有着一个重大的区别:在MVP中View并不直接使用Model,它们之间的通信是通过Presenter (MVC中的Controller)
来进行的,所有的交互都发生在Presenter内部,而在MVC中View会从直接Model中读取数据而不是通过 Controller。
区别如下图:(引用hongyang大神博文的的图片)
个人总结MVP的话其实用一句话来说明就可以了:大型项目,多人开发MVP必定是未来的趋势。如果是单人或者两个人开发的
话,个人觉得你用MVC也是很不错的,毕竟MVP开发成本太高,至少从类上来讲,那多出的类的个数差不多是一倍之差啊。
那说了这么多没用的,我们也来总结一下MVP的优点吧:
1、模型与视图完全分离,我们可以修改视图而不影响模型
2、可以更高效地使用模型,因为所有的交互都发生在一个地方——Presenter内部
3、我们可以将一个Presenter用于多个视图,而不需要改变Presenter的逻辑。这个特性非常的有用,因为视图的变化总是比模型
的变化频繁。
4、如果我们把逻辑放在Presenter中,那么我们就可以脱离用户接口来测试这些逻辑(单元测试)
Login MVP Demo
还有其他的一些概念性的东西也就不多说了,我们直接看Demo然后来真正的熟悉一下到底MVP开发模式是如何应用在实际的项
目中的,我这里以一个简单的一个登陆系统为例子来说一下吧。(本来想用自己个人项目的【比比账号通】的登录接口,但是为了
简单化,主要是了解MVP,所以我们就模拟一下吧)
首先来看一下我们要实现的效果:
这个功能如果大家用MVC来写,那肯定是分分钟的事情,但是如果是用MVP,可能就像我第一次自己写的的时候还真有点不好
写,下图是小项目的结构:
那么我们怎么来运用MVP呢,顺着我自己的思路一步一步来,首先既然是一个登陆系统,那么必然存在用户这个bean类,我们先
把bean类写好,如下:
package com.mic.blin.loginmvpdemo.bean;
/**
* Created by zhaocheng on 2016/2/18.
*/
public class User {
private String telephone;
private String password;
public String getTelephone() {
return telephone;
}
public void setTelephone(String telephone) {
this.telephone = telephone;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
(一)Modle
最简单的Bean写好,那么我们就要开始摸索这个MVP中的Modlel.
这个小项目就一个小功能,那就是登陆,那么是谁来用这个功能呢,很显然,不用多讲,新建IUser。如下:
package com.mic.blin.loginmvpdemo.model;
/**
* Created by zhaocheng on 2016/2/18.
*/
public interface IUser {
void login(String telephone,String password,OnLoginListener loginListener);
}
当然我这是直接写完后再把代码贴上来,给大家理一遍的,登录功能嘛,肯定要账号密码的,那么为什么还要一个借口呢,也很简
单,就是为了处理登录后,回调接口,用来处理登录后要处理的事情,对吧。应该不难理解的。
然后创建UserModel,去实现上面的接口,代码如下:
package com.mic.blin.loginmvpdemo.model;
import com.mic.blin.loginmvpdemo.bean.User;
/**
* Created by zhaocheng on 2016/2/22.
*/
public class UserModel implements IUser {
@Override
public void login(final String telephone, final String password, final OnLoginListener loginListener) {
//这边我们需要模拟一个耗时操作
new Thread()
{
@Override
public void run()
{
try
{
Thread.sleep(1000);
} catch (InterruptedException e)
{
e.printStackTrace();
}
//然后这边模拟登录成功
if ("blin".equals(telephone) && "000000".equals(password)){
User user = new User();
user.setTelephone(telephone);
user.setPassword(password);
loginListener.loginSuccess(user);
}else{
loginListener.loginFailed();
}
}
}.start();
}
}
上面的就是主要的Model了,我这边就是模拟了一下登录请求,用子线程睡眠模拟。不要吐槽,我们的重点不是在这里(。・∀・)ノ゙。
然后忘记了一个,上面的接口我们还没有定义呢,当然实际开发你肯定会先写这个回调接口的,不然看上IUser报错也是不爽,尤其是我这种有强迫症的人。
package com.mic.blin.loginmvpdemo.model;
import com.mic.blin.loginmvpdemo.bean.User;
/**
* Created by zhaocheng on 2016/2/18.
*/
public interface OnLoginListener {
void loginFailed();
void loginSuccess(User user);
}
Model其实就是这么简单的,和以往的写法也没什么区别是吧。
(二)View
接下来我们来看看View,其实呢没说白了,我们做这么多就是为了解耦,那么和View相关的事件有什么呢,以往可能直接在
Activity中写的和View有关的事件在MVP中都会通过接口,然后Activity再去实现。所以,刚开始不能理解,或者摸不着头脑的,
可以先按照以往的写法,然后再进去解耦。当然这样的方法肯定是违背了我们用MVP的初衷的。
我也就直接贴出干活了,大家自己看下也应该明白,就是把一些以往直接写在Activity中的和View有关的操作通过接口一一分开封装起来啦。
package com.mic.blin.loginmvpdemo.view;
import com.mic.blin.loginmvpdemo.bean.User;
/**
* Created by zhaocheng on 2016/2/22.
*/
public interface IUserLogin {
String getTelephone();
String getPassword();
void showLoading();
void hideLoading();
void toLoginFailedView();
void toLoginSuccessView(User user);
}
然后就是用Activity去实现啊这些方法了呗。
package com.mic.blin.loginmvpdemo.view;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.mic.blin.loginmvpdemo.R;
import com.mic.blin.loginmvpdemo.bean.User;
import com.mic.blin.loginmvpdemo.presenter.UserLoginPresenter;
public class MainActivity extends AppCompatActivity implements IUserLogin{
private Button btn_login;
private EditText et_account,et_password;
private UserLoginPresenter userLoginPresenter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
initEvent();
}
private void init(){
btn_login = (Button) findViewById(R.id.btn_login);
et_account = (EditText) findViewById(R.id.et_account);
et_password = (EditText) findViewById(R.id.et_password);
userLoginPresenter = new UserLoginPresenter(this);
}
private void initEvent(){
btn_login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
userLoginPresenter.login();
}
});
}
@Override
public String getTelephone() {
return et_account.getText().toString();
}
@Override
public String getPassword() {
return et_password.getText().toString();
}
@Override
public void showLoading() {
btn_login.setText("正在登陆。。。");
}
@Override
public void hideLoading() {
btn_login.setText("登录");
}
@Override
public void toLoginFailedView() {
Toast.makeText(this,"账号或密码错误!",Toast.LENGTH_SHORT).show();
}
@Override
public void toLoginSuccessView(User user) {
Toast.makeText(this,"欢迎"+user.getTelephone()+"正在跳转。。。",Toast.LENGTH_SHORT).show();
}
}
当然我们的Presenter还没写,我就直接把整个Avtivity贴出来了。。。有点不妥。我们从上面的点击事件中可以看到,点击以后就
是通过userLoginPresenter去调用登录方法了。所以真的在Activity中的逻辑你都看不到了,全都的业务都有Presenter接手。
(三)Presenter
最后就是我们的Presenter了,他的出现就是为了帮助我们处理Model和View之间的数据交互啦,基本上所有的业务也都在这里了。
package com.mic.blin.loginmvpdemo.presenter;
import android.os.Handler;
import com.mic.blin.loginmvpdemo.bean.User;
import com.mic.blin.loginmvpdemo.model.OnLoginListener;
import com.mic.blin.loginmvpdemo.model.UserModel;
import com.mic.blin.loginmvpdemo.view.IUserLogin;
/**
* Created by zhaocheng on 2016/2/22.
*/
public class UserLoginPresenter {
private UserModel userModel;
private IUserLogin userView;
public User user;
private Handler handler = new Handler();
public UserLoginPresenter(IUserLogin userView){
userModel = new UserModel();
this.userView = userView;
this.user = new User();
}
public void login(){
userView.showLoading();
userModel.login(userView.getTelephone().toString(), userView.getPassword().toString(), new OnLoginListener() {
@Override
public void loginFailed() {
//注意这里,我们是模拟了延迟操作,而且是在子线程,所以这里要调用改变UI的操作必须回到主线程。
handler.post(new Runnable() {
@Override
public void run() {
userView.toLoginFailedView();
userView.hideLoading();
}
});
}
@Override
public void loginSuccess(final User user) {
//同上
handler.post(new Runnable() {
@Override
public void run() {
userView.toLoginSuccessView(user);
userView.hideLoading();
}
});
}
});
}
}
业务就是一个登录,对的,那就来一个登录的方法接口呗。用我们最简单的逻辑去想一下,当点击登录以后,然后调用登录方法,
那么在Presenter中肯定有我们在Activity中实现的接口对象,还有就是Model对象。还有就是那个回调接口直接在这里被初始化
传入,然后处理登录后处理相应的操作,就是因为接口对象的存在,才使得Activity中的方法在Presenter中被轻松的调用到。
总的来说,对于刚入门的,可能这几个接口就把你搞晕了,的确是有点绕,比直接使用我们习惯的MVC的确要夺走一点路子。但是这些东西也是值得的,试想,本来一个页面中的东西可能用MVC开发,等到大家都签入的时候,各种冲突,然而如果是用MVP
开发的话,大家同时使用的就那个Activity,就是每个人多了一些业务接口呗。
大家刚接触的绝对不要慌,这个东西其实昨天和我朋友两个人也聊了很多,的确发现很多年以避免的问题,尤其是类爆炸,或者是当业务逻辑比较复杂时,自己都会搞晕,或者说为了写一个简单的功能浪费了2倍的时间等等,但是作为一个Android的 不管以后是不是会成为主流,但是相比还是要了解一下的,对吧。
PS
好啦,这是2016的第一篇,今天又是元宵,祝大家节日快乐哈~
代码下载地址:
Android MVP 下载