Android开发模式MVP,MVC
2016-12-27 21:48
465 查看
一、概述
Android MVP 模式 也不是什么新鲜的东西了,当然现在也很多的项目中会用到这个模式,这个模式适合在当项目越来越庞大、复杂,参与的研发人员越来越多的时候,它的优势就会充分显示出来。如果你在公司是自己一个或者两个开发时,我觉得使用这个MVP模式的话效果不会很明显,而且反而有时会让人感觉有点臃肿,因为它需要定义的接口和presenter会比较多,好了一起来认识下它吧。
MVP模式是由MVC模式转换过来的,在MVC模式中主要分为model,view,controller,而MVP模式主要是分为了View,Model,Presenter,二者的区别:
MVP模式:
1)、View不直接与Model交互,而是通过与Presenter交互来与Model间接交互
2)、Presenter与View的交互是通过接口来进行的,更有利于添加单元测试
3)、通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑
MVC模式:
1)、View可以与Model直接交互
2)、Controller是基于行为的,并且可以被多个View共享
3)、可以负责决定显示哪个View
使用MVP模式的好处:
1)、分离了视图逻辑和业务逻辑,降低了耦合:随着界面及其逻辑的复杂度不断提升,Activity类的 职责不断增加,以致变得庞大臃肿
2)、模块职责划分明显:View,Presenter,Model
3)、Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
4)、Activity只处理生命周期的任务,代码变得更加简洁
5)、视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
6)、把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM
其中最重要的有三点:
Activity 代码变得更加简洁
相信很多人阅读代码的时候,都是从Activity开始的,对着一个1000+行代码的Activity,看了都觉得难受。
使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。
方便进行单元测试
一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……
MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧。
避免 Activity 的内存泄露
Android APP 发生OOM的最大原因就是出现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak))。
Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM。
采用传统的MV模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity
Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM。
采用MVP模式,只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak。
好了,接下来我们一起来看下代码吧
二、MVP简单案例
使用MVP做一个登陆的功能,当然在项目中需要根据实际情况来封装
1、项目结构
2、model层
包含有一个实体UserModel用来承载数据和对登陆数据判断
把View层针对控件操作抽象出来一些列的接口,根据实际的需要来进行设计
3、Presenter层
在Presenter层里面,Presenter掌握着View和Model的所有接口,可以根据不同的业务逻辑通过model和view两层的接口来实现特定的功能
从代码可以看出,LoginPresenterCompl保留了ILoginView的引用,因此在LoginPresenterCompl里就可以直接进行UI操作了,而不用在Activity里完成。这里使用了ILoginView引用,而不是直接使用Activity,这样一来,如果在别的Activity里也需要用到相同的业务逻辑,就可以直接复用LoginPresenterCompl类了
三、总结
可以看到我们的Activity非常的简洁,到了这里我们的项目已经搭建完成,这样做有以下好处:
1、学习过设计模式的人都知道,这样做基本符合了单一职责原则。
2、符合单一职责原则后,导致类与类组织更清晰。
3、View层与Model层交互需要通过Presenter层进行,这样v与m层级间的耦合性降低。
4、通过这种分层处理,每一层的测试也相对简单,维护性更高。
Demo下载地址:点击下载源码
Android MVP 模式 也不是什么新鲜的东西了,当然现在也很多的项目中会用到这个模式,这个模式适合在当项目越来越庞大、复杂,参与的研发人员越来越多的时候,它的优势就会充分显示出来。如果你在公司是自己一个或者两个开发时,我觉得使用这个MVP模式的话效果不会很明显,而且反而有时会让人感觉有点臃肿,因为它需要定义的接口和presenter会比较多,好了一起来认识下它吧。
MVP模式是由MVC模式转换过来的,在MVC模式中主要分为model,view,controller,而MVP模式主要是分为了View,Model,Presenter,二者的区别:
MVP模式:
1)、View不直接与Model交互,而是通过与Presenter交互来与Model间接交互
2)、Presenter与View的交互是通过接口来进行的,更有利于添加单元测试
3)、通常View与Presenter是一对一的,但复杂的View可能绑定多个Presenter来处理逻辑
MVC模式:
1)、View可以与Model直接交互
2)、Controller是基于行为的,并且可以被多个View共享
3)、可以负责决定显示哪个View
使用MVP模式的好处:
1)、分离了视图逻辑和业务逻辑,降低了耦合:随着界面及其逻辑的复杂度不断提升,Activity类的 职责不断增加,以致变得庞大臃肿
2)、模块职责划分明显:View,Presenter,Model
3)、Presenter被抽象成接口,可以有多种具体的实现,所以方便进行单元测试
4)、Activity只处理生命周期的任务,代码变得更加简洁
5)、视图逻辑和业务逻辑分别抽象到了View和Presenter的接口中去,提高代码的可阅读性
6)、把业务逻辑抽到Presenter中去,避免后台线程引用着Activity导致Activity的资源无法被系统回收从而引起内存泄露和OOM
其中最重要的有三点:
Activity 代码变得更加简洁
相信很多人阅读代码的时候,都是从Activity开始的,对着一个1000+行代码的Activity,看了都觉得难受。
使用MVP之后,Activity就能瘦身许多了,基本上只有FindView、SetListener以及Init的代码。其他的就是对Presenter的调用,还有对View接口的实现。这种情形下阅读代码就容易多了,而且你只要看Presenter的接口,就能明白这个模块都有哪些业务,很快就能定位到具体代码。Activity变得容易看懂,容易维护,以后要调整业务、删减功能也就变得简单许多。
方便进行单元测试
一般单元测试都是用来测试某些新加的业务逻辑有没有问题,如果采用传统的代码风格(习惯性上叫做MV模式,少了P),我们可能要先在Activity里写一段测试代码,测试完了再把测试代码删掉换成正式代码,这时如果发现业务有问题又得换回测试代码,咦,测试代码已经删掉了!好吧重新写吧……
MVP中,由于业务逻辑都在Presenter里,我们完全可以写一个PresenterTest的实现类继承Presenter的接口,现在只要在Activity里把Presenter的创建换成PresenterTest,就能进行单元测试了,测试完再换回来即可。万一发现还得进行测试,那就再换成PresenterTest吧。
避免 Activity 的内存泄露
Android APP 发生OOM的最大原因就是出现内存泄露造成APP的内存不够用,而造成内存泄露的两大原因之一就是Activity泄露(Activity Leak)(另一个原因是Bitmap泄露(Bitmap Leak))。
Java一个强大的功能就是其虚拟机的内存回收机制,这个功能使得Java用户在设计代码的时候,不用像C++用户那样考虑对象的回收问题。然而, Java用户总是喜欢随便写一大堆对象,然后幻想着虚拟机能帮他们处理好内存的回收工作。可是虚拟机在回收内存的时候,只会回收那些没有被引 用的对象,被引用着的对象因为还可能会被调用,所以不能回收。
Activity是有生命周期的,用户随时可能切换Activity,当APP的内存不够用的时候,系统会回收处于后台的Activity的资源以避免OOM。
采用传统的MV模式,一大堆异步任务和对UI的操作都放在Activity里面,比如你可能从网络下载一张图片,在下载成功的回调里把图片加载到 Activity 的 ImageView 里面,所以异步任务保留着对Activity的引用。这样一来,即使Activity已经被切换到后台(onDestroy已经执行),这些异步任务仍然保留着对Activity实例的引用,所以系统就无法回收这个Activity实例了,结果就是Activity
Leak。Android的组件中,Activity对象往往是在堆(Java Heap)里占最多内存的,所以系统会优先回收Activity对象,如果有Activity Leak,APP很容易因为内存不够而OOM。
采用MVP模式,只要在当前的Activity的onDestroy里,分离异步任务对Activity的引用,就能避免 Activity Leak。
好了,接下来我们一起来看下代码吧
二、MVP简单案例
使用MVP做一个登陆的功能,当然在项目中需要根据实际情况来封装
1、项目结构
2、model层
包含有一个实体UserModel用来承载数据和对登陆数据判断
public class UserModel implements IUser { String name; String passwd; public UserModel(String name, String passwd) { this.name = name; this.passwd = passwd; } @Override public String getName( 4000 ) { return name; } @Override public String getPasswd() { return passwd; } @Override public int checkUserValidity(String name, String passwd){ if (name==null||passwd==null||!name.equals(getName())||!passwd.equals(getPasswd())){ return -1; } return 0; } }2、view层
把View层针对控件操作抽象出来一些列的接口,根据实际的需要来进行设计
public interface ILoginView { public void onClearText(); public void onLoginResult(Boolean result, int code); public void onSetProgressBarVisibility(int visibility); }在Activity里面实现该接口的控件操作,并且初始化Presenter,这是可以看到Activity里面没有逻辑处理,只是对UI的控件进行数据或者行为的操作,所有的动作都是有Presenter的接口来实现,这样在项目里面会极大得精简Activity的体积.
public class MainActivity extends AppCompatActivity implements ILoginView,View.OnClickListener { private EditText editUser; private EditText editPass; private Button btnLogin; private Button btnClear; ILoginPresenter loginPresenter; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initView(); } private void initView() { editUser = (EditText) this.findViewById(R.id.et_login_username); editPass = (EditText) this.findViewById(R.id.et_login_password); btnLogin = (Button) this.findViewById(R.id.btn_login_login); btnClear = (Button) this.findViewById(R.id.btn_login_clear); progressBar = (ProgressBar) this.findViewById(R.id.progress_login); //set listener btnLogin.setOnClickListener(this); btnClear.setOnClickListener(this); loginPresenter = new LoginPresenterCompl(this); } @Override public void onClearText() { editUser.setText(""); editPass.setText(""); } @Override public void onLoginResult(Boolean result, int code) { loginPresenter.setProgressBarVisiblity(View.INVISIBLE); btnLogin.setEnabled(true); btnClear.setEnabled(true); if (result){ Toast.makeText(this,"登陆成功",Toast.LENGTH_SHORT).show(); } else Toast.makeText(this,"登陆失败, code = " + code,Toast.LENGTH_SHORT).show(); } @Override public void onSetProgressBarVisibility(int visibility) { progressBar.setVisibility(visibility); } @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_login_clear: loginPresenter.clear(); break; case R.id.btn_login_login: btnLogin.setEnabled(false); btnClear.setEnabled(false); loginPresenter.doLogin(editUser.getText().toString(), editPass.getText().toString()); loginPresenter.setProgressBarVisiblity(View.VISIBLE); break; } } }
3、Presenter层
在Presenter层里面,Presenter掌握着View和Model的所有接口,可以根据不同的业务逻辑通过model和view两层的接口来实现特定的功能
public interface ILoginPresenter { void clear(); void doLogin(String name, String passwd); void setProgressBarVisiblity(int visiblity); }
public class LoginPresenterCompl implements ILoginPresenter { ILoginView iLoginView; IUser user; Handler handler; public LoginPresenterCompl(ILoginView iLoginView) { this.iLoginView = iLoginView; initUser(); handler = new Handler(Looper.getMainLooper()); } @Override public void clear() { iLoginView.onClearText(); } @Override public void doLogin(String name, String passwd) { Boolean isLoginSuccess = true; final int code = user.checkUserValidity(name,passwd); if (code!=0) isLoginSuccess = false; final Boolean result = isLoginSuccess; handler.postDelayed(new Runnable() { @Override public void run() { iLoginView.onLoginResult(result, code); } }, 5000); } @Override public void setProgressBarVisiblity(int visiblity){ iLoginView.onSetProgressBarVisibility(visiblity); } private void initUser(){ user = new UserModel("mvp","mvp"); } }
从代码可以看出,LoginPresenterCompl保留了ILoginView的引用,因此在LoginPresenterCompl里就可以直接进行UI操作了,而不用在Activity里完成。这里使用了ILoginView引用,而不是直接使用Activity,这样一来,如果在别的Activity里也需要用到相同的业务逻辑,就可以直接复用LoginPresenterCompl类了
三、总结
可以看到我们的Activity非常的简洁,到了这里我们的项目已经搭建完成,这样做有以下好处:
1、学习过设计模式的人都知道,这样做基本符合了单一职责原则。
2、符合单一职责原则后,导致类与类组织更清晰。
3、View层与Model层交互需要通过Presenter层进行,这样v与m层级间的耦合性降低。
4、通过这种分层处理,每一层的测试也相对简单,维护性更高。
Demo下载地址:点击下载源码
相关文章推荐
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
- Android开发框架模式MVC、MVP使用总结
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
- 浅谈Android开发中的MVVM模式及与MVP和MVC的区别
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
- 浅谈Android开发中内存泄露与优化与框架模式之MVC与MVP
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
- MVP 模式(Model-View-Presenter)可以说是 MVC 模式(Model-View-Controller)在 Android 开发上的一种变种、进化模式。
- Android开发模式之MVC,MVP和MVVM的简单介绍与区别
- Android——开发模式mvc和mvp
- 也谈Android开发之MVP模式
- MVP模式在Android开发中的应用
- Android 基于MVC的MVVM模式开发
- MVP模式在Android开发中的最佳实践
- 详解Android MVP开发模式
- Android中的 mvp 开发模式
- MVP模式在Android开发中的最佳实践
- Android实现竖着的滑动刻度尺效果,选择身高(竖向的)Android实现滑动刻度尺效果,选择身高体重和生日------浅谈Andorid开发中的MVP模式