关于Android MVP模式的思考
2016-05-05 11:18
597 查看
最近经常看到各种介绍MVP模式的博客的,之前写过不少的Android应用,在做那些应用的时候,都是要求快速完成,所以从开始设计到写代码就一直考虑着重用。以前写的项目基本都是不断重构项目,将项目代码变得更加精简,提高代码之间的复用性。但是代码并没有特别地注重按照MVC模式或者是MVP模式来,更多的是直接考虑模块化,重用,精简。所以看了MVP模式后,决定去总结一下自己代码中的问题并优化,算是对自己之前写的代码的回顾。
- M(Model):表示数据模型,以及相关的数据结构。
- V(View):表示视图,主要是指UI界面相关的那些东西。在Android里面比如说layout的xml文件,在MVP中,很多时候Activity/Fragment也是被看做View。
- P(Presenter):可以直接理解为视图与模型的中间纽带。
在MVP模式中,Model和View不直接进行关联,而是通过Presenter来进行关联的。往往实现的方式是增加一个IModel接口和IView接口,用Model和View分别实现那两个接口,Presenter里面保存IModel和IView接口类型的成员变量。
用户的JavaBean—User:
用户模型接口
真正模型
Presenter:
把Activity作为View来看待
上面代码中Activity不再与Model直接关联,而是通过Presenter来间接关联Model。并且当Model的数据变化了的时候,Presenter能够通知View。上面的例子是View需要变化了,请求Presenter获取数据。
上面是关于代码方面的优势,其实通过分隔开Model和View,也是将各个模块进行了解耦。另外一方面通过增加IUserView和IUserModel,这样每个部分进行单元测试也更加方便了,比如可以直接实现IUserView来模拟测试Presenter。
个人觉得MVP模式适合Android应用,一个很大的原因就是Activity这种组件的存在,UI交互完全放在了Activity里面,这导致很多时候Activity会在一不注意间就变得臃肿。View过于庞大。当然还有很多跟Android相关的优势,比如说能够更好地避免Activity的内存泄漏(Presenter直接引用View,而不是Activity的时候)。其实通过增加Presenter,一定程度上也增加了Presenter的复用,很多人说View和Presenter是一一对应的,但是我觉得如果不一一对应,比如说一个Activity里面包含多个Presenter,将Presenter细化,那样Presenter复用的可能性也就越高了,同时也避免了Presenter过于臃肿。
先模块化将程序分为Fragment/Activity部分,Adapter部分,模型网络操作部分,通过将相关类进行层次化来减少类的臃肿,另外通过AOP编程的方式将一些内容从子父类中提取出来,作为一个单独的切面。这种面向切面能够提高代码的复用性。
上面的类型图里面也有一个不合理的例子,正如上面的注释中所说的将网络操作作为模型的一部分,而不是再添加一个接口,会导致网络操作一旦修改,会影响到Model的子类。
下面看看如何对上面的结构进行转换。上面这种结构更多的是MVC的方式,而且是将V和C基本合在一起了,内聚在一起能够更方面UI交互,但是也会导致臃肿。上面的图中发现只有BaseFragment单向地去关联Model,这样势必会导致Fragment增加更多的代码来控制,因为在Android中进行网络连接是一定要在非UI线程操作的,如果Model没有关连Fragment,必然需要将一个异步操作放到Fragment,等待Model操作完成了后,Fragment来更新Fragment的内容。如果增加一个接口,然后Fragment实现,并且用Model引用它,那么将能够减少Fragment的操作,而且能够更好地去对Model进行单元测试。如果增加一个Presenter,由Presenter与Fragment相互关联,然后Presenter也与Model相互关联,那么Fragment将会大大简化,更加专注一些其他的UI交互。增加Presenter整个架构就变成了MVP模式了。重构后的类型图如下:
MVP框架
MVP框架是目前在Android流行起来的框架,它非常适合用于Android开发上面。我最早接触MVP模式是在一本敏捷开发的书上。MVP分别指代M(model),V(View),P(Presenter):- M(Model):表示数据模型,以及相关的数据结构。
- V(View):表示视图,主要是指UI界面相关的那些东西。在Android里面比如说layout的xml文件,在MVP中,很多时候Activity/Fragment也是被看做View。
- P(Presenter):可以直接理解为视图与模型的中间纽带。
在MVP模式中,Model和View不直接进行关联,而是通过Presenter来进行关联的。往往实现的方式是增加一个IModel接口和IView接口,用Model和View分别实现那两个接口,Presenter里面保存IModel和IView接口类型的成员变量。
代码示例
下面举一个关于Android用户信息的MVP模式例子。public interface IUserView{ void setName(String name); }
用户的JavaBean—User:
class User{ private String name; private String id; public String getName(){ return name; } public String getId(){ return id; } public void setName(String name){ this.name = name; } public void setId(String id){ this.id = id; } }
用户模型接口
public interface IUserModel{ void saveUser(User user); User load(); }
真正模型
public class UserModel implements IUserModel{ void saveUser(User user){ // 保存用户信息,保存在本地数据库中,或者xml里面 } User loadUser(){ // 从网络中,或者从本地缓存中读取用户信息 return null; //未实现 } }
Presenter:
public class UserPresenter{ private IUserView view; private IUserModel userModel; public UserPresenter(IUserView view){ this.view = view; userModel = new UserModel(); } public void loadUser(){ // 此处可能是异步load User user = userModel.loadUser(); view.setName(user.getName()); } }
把Activity作为View来看待
public class UserActivity extends Activity implements IUserView{ TextView tvName; //名称对应的TextView UserPresenter userPresenter ; public void onCreate(Bundle bundle){ ... userPresenter = new UserPresenter(this); // userPresenter.loadUser(); 可以有先初始化 } void setName(String name){ tvName.setText(name); //如果presenter的loadUser是异步线程,这里可以通过tvName.post来运行在UI线程。 } void reloadUserData(){ //该函数可以某个按钮的onClick事件里面调用 userPresenter.loadUser(); // 可以是从网络中加载数据 } }
上面代码中Activity不再与Model直接关联,而是通过Presenter来间接关联Model。并且当Model的数据变化了的时候,Presenter能够通知View。上面的例子是View需要变化了,请求Presenter获取数据。
分析
MVP与MVC最大的不同就是View和Model不再直接关联。很多Android的MVC模式都是直接将Activity看作Control,这会导致整个Activity非常臃肿,因为它既然进行UI交互,还需要加上Control这部分的功能。另外即使另外新建一个Control,而把Activity只当做View的功能,如果Activity还是直接跟Model直接关联的话,因为跟Model直接关联,还是会在Activity增加很多操作。而使用P作为Model和View的纽带,P可以先对模型数据进行一些处理,然后再显示到View。另外一方面对于View的一些请求,Presenter也可以进行一些处理再去请求Model。上面是关于代码方面的优势,其实通过分隔开Model和View,也是将各个模块进行了解耦。另外一方面通过增加IUserView和IUserModel,这样每个部分进行单元测试也更加方便了,比如可以直接实现IUserView来模拟测试Presenter。
个人觉得MVP模式适合Android应用,一个很大的原因就是Activity这种组件的存在,UI交互完全放在了Activity里面,这导致很多时候Activity会在一不注意间就变得臃肿。View过于庞大。当然还有很多跟Android相关的优势,比如说能够更好地避免Activity的内存泄漏(Presenter直接引用View,而不是Activity的时候)。其实通过增加Presenter,一定程度上也增加了Presenter的复用,很多人说View和Presenter是一一对应的,但是我觉得如果不一一对应,比如说一个Activity里面包含多个Presenter,将Presenter细化,那样Presenter复用的可能性也就越高了,同时也避免了Presenter过于臃肿。
一个模式优化过程
平时自己写代码的时候,其实还真的是比较少那么明显地使用这种MVP模式,或者说是MVC模式。更多的是模块化,类层次化(面向对象),面向切面,并且坚持代码精简,复用的原则。下面介绍一个将平时的项目简化后的模式,并且通过考虑优化结构将模式转变成MVP的过程。先看看下面这种类型结构:先模块化将程序分为Fragment/Activity部分,Adapter部分,模型网络操作部分,通过将相关类进行层次化来减少类的臃肿,另外通过AOP编程的方式将一些内容从子父类中提取出来,作为一个单独的切面。这种面向切面能够提高代码的复用性。
上面的类型图里面也有一个不合理的例子,正如上面的注释中所说的将网络操作作为模型的一部分,而不是再添加一个接口,会导致网络操作一旦修改,会影响到Model的子类。
下面看看如何对上面的结构进行转换。上面这种结构更多的是MVC的方式,而且是将V和C基本合在一起了,内聚在一起能够更方面UI交互,但是也会导致臃肿。上面的图中发现只有BaseFragment单向地去关联Model,这样势必会导致Fragment增加更多的代码来控制,因为在Android中进行网络连接是一定要在非UI线程操作的,如果Model没有关连Fragment,必然需要将一个异步操作放到Fragment,等待Model操作完成了后,Fragment来更新Fragment的内容。如果增加一个接口,然后Fragment实现,并且用Model引用它,那么将能够减少Fragment的操作,而且能够更好地去对Model进行单元测试。如果增加一个Presenter,由Presenter与Fragment相互关联,然后Presenter也与Model相互关联,那么Fragment将会大大简化,更加专注一些其他的UI交互。增加Presenter整个架构就变成了MVP模式了。重构后的类型图如下:
总结
模式总是坚持着复用,模块间低耦合,模块内高内聚等等原则来进行的,设计模式中就有六大原则: 单一职责原则,开闭原则,依赖倒转原则,迪米特法则,里氏替换原则,组合聚合原则。好的模式能够让人在阅读的时候能够很好地理解代码,在对程序进行修改的时候能够快速简洁,并且不对原有代码结构破坏。相关文章推荐
- Android Studio使用教程2
- 使用PendingIntent.getBrocast() 在 onReceive 中接受不到intent 的问题
- Android文件存储的问题:ClassLoader和实现Parcelable接口后 详解及用途
- Android性能优化之一:ViewStub
- android瀑布流效果(仿蘑菇街)
- 关于android Imageview 圆角圆图处理xutils兼容问题
- Android中的SurfaceView学习
- Android LayoutInflater原理分析,带你一步步深入了解View(一) ---站在巨人的肩膀上学习总结
- Android--自定义控件---自动分页的GridView
- Android 异步加载图片,使用LruCache和SD卡或手机缓存,效果非常的流畅
- android popupwindow的实现
- Android随手记
- Android的apk文件是怎么怎么生成的?
- Android 高手进阶之自定义View,自定义属性(带进度的圆形进度条)
- Android仿QQ附近的人搜索展示功能
- Android学习路线指南
- android relativelayout属性大全
- Android自定义View之圆环交替 等待效果
- Android 屏幕操作原理
- ViewPager动画之三个item的实现