Android中的MVP架构初探
说来惭愧,MVP的架构模式已经在Android领域出现一两年了,但是到今天自己才开始Android领域中的MVP架构征程。闲话不多说,开始吧!
一、架构演变概述
我记得我找第一份工作时,面试官问我“android是否属于MVC架构模式,简述一下”。确实,Android的整体设计结构就是MVC的设计模式,在J2EE的开发中,使用的也是MVC模式,MVC模式是一个经典,经历了几十年的考验。Android项目中的MVC架构:
- View:是应用程序中处理数据显示的部分,对应于layout文件下的布局文件
- Model:业务逻辑和实体模型
- Controllor:是应用程序中处理用户交互的部分,Activity来充当。
看似分工明确,但是也给我们带来了不少问题,如果一个页面的业务逻辑非常复杂,我们的Activty需要大量的逻辑处理代码,使得Activity既像View又像Controllor,又当爹又当妈,工作量非常大。造成代码的阅读星非常差。
为了解决这个问题,MVP模式就在Android领域诞生了。补充:我查看资料发现MVP模式最早是微软设计出来的,应用在Visual Stuido平台,不得不说微软虽然闭源,但是很niubility。
二、MVP概述
上面的图,精炼的概述了MVP架构之间的通讯流程,在android中。
- 模型(Model):业务逻辑处理,负责处理数据的加载或者存储,比如从网络或本地数据库获取数据等;
- 视图(View):负责界面数据的展示,与用户进行交互,就是Activity;
- 主导器(Presenter):相当于协调者,是模型与视图之间的桥梁,将模型与视图分离开来。通过Presenter进行它们之间的交互,隔离了M、V之间的直接交互。
这样的架构,就让我们的Activity更像View,降低了M、V之间的耦合度。
三、MVP实践
我们先看下我们的效果图:
这是一个简答的登陆页面,在这个登陆页面中,我们就触发一个事件,就是点击登陆事件。我们先看看我们的工程组织架构。
我们分别创建了:bean、model、presenter、view四个包,为了解放Activity,我们把以前Activity里的大量逻辑进行拆分。
1、bean包
我们创建实体UserBean,用来存放我们的用户信息,这个没什么说的。
public class UserBean implements Parcelable{ private String name; private String password; public UserBean(){ } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(name); dest.writeString(password); } }
2、view包
我们首先从View(视图层)看,view包下存放了我们视图层中的操作,它不涉及任何业务逻辑,主要是针对我们的页面进行数据的获取与设置。我们针对这个登陆功能,进行分析:
- 姓名和密码的获取
- 姓名和密码的保存
我们抽取出页面View层需要做的事情,接下来我们定义一个IUserView接口,里面包含我们抽取出来的方法。
/** * 抽离View层,用于View页面的数据获取之类 * @author Administrator * */ public interface IUserView { public String getUserName(); public String getUserPsd(); }
我们只进行了获取,没有做保存。总结:View层的任务就是抽象页面的数据,提取出来写成方法。
3、model包
在view层,我们的数据获取有了着落,现在我们就开始处理我们的业务逻辑。根据页面得知,我们就模拟一个登陆功能,所以它就一个登陆事件。我们创建IUserModel接口用于包含我们处理的业务逻辑。
/** * 业务逻辑处理 * @author Administrator * */ public interface IUserModel { /** *提取的一个登陆方法,当然还可以有其它方法,比如获取数据,保存用户信息之类 * @param name 用户名 * @param pwd 密码 * @param loginListener 登陆监听 */ public void login(String name,String pwd,ILoginListener loginListener); }
有了业务逻辑的接口规范,我们还需要去实现它,给与我们想要的逻辑。所以就有了UserModel类:
public class UserModel implements IUserModel{ @Override public void login(String name, String pwd, ILoginListener loginListener) { if(TextUtils.isEmpty(name) || TextUtils.isEmpty(pwd)){ loginListener.onError(); return; } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } if("dsw".equals(name) && "123".equals(pwd)){ loginListener.onSucess(); }else{ loginListener.onFail(); } } }
我们为了方便我们对业务逻辑处理的监听,所以我们创建了一个接口,用来判断我们的登陆事件。
/** * 登陆接口的监听,方便我们在View层中控制,便于给出提示 * @author Administrator * */ public interface ILoginListener { //登陆成功 public void onSucess(); //登陆失败 public void onFail(); //数据不完成 public void onError(); }
我们用来监听业务处理的判断,比如成功了我们弹出一个Toast之类的。
4、presenter包
数据有了,对数据的业务处理也有了,这里我们的Presenter就闪灵登场了。记住是Presenter、Presenter、Presenter(重要的东西说三次)。它用来连接我们的M、V层,让二者打通任督二脉实现整个流程。所以为了实现二者的联系,它的内部必然会有M、V的实例。来看看IUserPresenter:
public class IUserPresenter { //数据源 private IUserView userView; //处理业务逻辑 private IUserModel userModel; public IUserPresenter(IUserView userView){ this.userView = userView; userModel = new UserModel(); } /** * 登陆方法,进行M,V层的关系建立 * @param loginListener */ public void login(ILoginListener loginListener){ userModel.login(userView.getUserName(), userView.getUserPsd(), loginListener); } }
我们只需要在Activity中创建一个IUserPresenter类,然后调用login方法进行登录即可。在这段代码中,IUserView是我们的数据源,我们通过它的方法用于从页面获取数据,所以我们的Activity必须实现这个IUserView接口,然后传递给IUserPresenter。在IUserPresenter中调用IUserModel的实现业务逻辑的处理。
5、最后的Activity处理
public class MainActivity extends Activity implements IUserView { private EditText et_name,et_pwd; private Button btn_login; //指示器与View层进行交互 private IUserPresenter userPresenter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); et_name = (EditText) findViewById(R.id.et_name); et_pwd = (EditText) findViewById(R.id.et_psw); btn_login = (Button) findViewById(R.id.btn_login); userPresenter = new IUserPresenter(this); btn_login.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { userPresenter.login(loginListener); } }); } @Override public String getUserName() { return et_name.getText().toString(); } @Override public String getUserPsd() { return et_pwd.getText().toString(); } /** * 登陆监听接口 */ private ILoginListener loginListener = new ILoginListener() { @Override public void onSucess() { Toast.makeText(getApplication(), "登陆成功", Toast.LENGTH_SHORT).show(); } @Override public void onFail() { Toast.makeText(getApplication(), "登陆失败", Toast.LENGTH_SHORT).show(); } @Override public void onError() { Toast.makeText(getApplication(), "数据不完整,请重新输入", Toast.LENGTH_SHORT).show(); } }; }
在activity中,我们实现IUserView接口,来实现该view层的数据获取。然后创建IUserPresenter类,调用login方法进行登录。同时实现对业务逻辑的处理监听。
至此,我们就完成了整个的流程,看看我们的效果图:
源码本来打算上传的熬csdn的,结果上传不了,所以果断github
强烈建议大家手动敲一遍代码,加深理解。
总结图:
==========================================================
作者:mr_dsw
转载注明出处,分享是进步的源泉。
==========================================================
更多相关文章
- android游戏开发(三)触屏事件处理_手势识别
- Android(安卓)call setting 源码分析 从顶层到底层(下)
- Android接入WebView(四)——浏览器书签与历史记录详细处理
- 让android支持RTSP及live555分析
- android进阶4step2:Android音视频处理——音频管理
- Android消息机制浅析——面试总结
- Android(安卓)滑动侧边栏(Sliding Menu)第一种实现 - 2 手动滚动+
- Android消息机制浅析——面试总结
- Android(安卓)应用启动闪白一下处理方法