第二十六章、MVP应用构架模式

来源:互联网 发布:湘北vs陵南数据 编辑:程序博客网 时间:2024/05/02 00:01

转载于:http://blog.csdn.net/qq_17766199/article/details/50591910


1.MVP介绍

MVP模式是MVC模式的一个演化版本,MVP全称Model-View-Presenter。目前MVP在Android应用开发中越来越重要了。

在Android中,业务逻辑和数据存取是紧紧耦合的,很多缺乏经验的开发者很可能会将各种各样的业务逻辑塞进某个Activity、Fragment或者自定义View中,这样会使得这些组件的单个类型臃肿不堪。如果不将具体的业务逻辑抽离出来,当UI变化时,你就需要去原来的View中抽离具体业务逻辑,这必然会很麻烦并且易出错。

2.使用MVP的好处

(1)MVP模式会解除View与Model的耦合,有效的降低View的复杂性。同时又带来了良好的可扩展性、可测试性,保证系统的整洁性和灵活性。

(2)MVP模式可以分离显示层与逻辑层,它们之间通过接口进行通信,降低耦合。理想化的MVP模式可以实现同一份逻辑代码搭配不同的显示界面,因为它们之间并不依赖与具体,而是依赖于抽象。这使得Presenter可以运用于任何实现了View逻辑接口的UI,使之具有更广泛的适用性,保证了灵活度。

3.MVP模式的三个角色

(1)Presenter – 交互中间人:Presenter主要作为沟通ViewModel的桥梁,它从Model层检索数据后,返回给View层,使得ViewModel之间没有耦合,也将业务逻辑从View角色上抽离出来。

(2)View – 用户界面:View通常是指ActivityFragment或者某个View控件,它含有一个Presenter成员变量。通常View需要实现一个逻辑接口,将View上的操作转交给Presenter进行实现,最后,Presenter 调用View逻辑接口将结果返回给View元素。

(3)Model – 数据的存取:Model 角色主要是提供数据的存取功能。Presenter 需要通过Model层存储、获取数据,Model就像一个数据仓库。更直白的说,Model是封装了数据库DAO或者网络获取数据的角色,或者两种数据方式获取的集合。

4.与MVC、MVVM的区别

1.与MVC的区别

这里写图片描述

从上图可以看出:MVC的耦合性还是较高的,View可以直接访问Model,导致3者之间构成了回路。所以两者的主要区别是,MVP中View不能直接访问Model,需要通过Presenter发出请求,View与Model不能直接通信。

2.与MVVM(Model-View-ViewModel)的区别

MVVM与MVP非常相似,唯一区别是View和Model进行双向绑定,两者之间有一方发生变化则会反应到另一方上。MVVM模式有点像ListView与Adapter、数据集的关系,当数据集发生变化时,调用Adapter的notifyDataSetChanged之后View就直接更新,同时它们之间又没有耦合,使得ListView变得更加灵活。

5.MVP简单实现

可以参考:

1. androidmvp

2. archi

6.MVP 与Activity、Fragment的生命周期

由于Presenter 经常性的持有Activity 的强引用,如果在一些请求结束之前Activity 被销毁了,那么Presenter 一直持有Activity 对象,使得Activity对象无法回收,此时就会发生内存泄露。

那么解决方法就是采用弱引用和Activity、Fragment的生命周期来解决这个问题。首先建立一个Presenter 抽象:

<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">abstract</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> BasePresenter<T> {    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> Reference<T> mViewRef; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//View接口类型的弱引用</span>    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">attachView</span>(T view){        mViewRef = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> WeakReference<T>(view); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//建立关联</span>    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> T <span class="hljs-title" style="box-sizing: border-box;">getView</span>(){        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mViewRef.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//获取View</span>    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> boolean <span class="hljs-title" style="box-sizing: border-box;">isViewAttached</span>(){        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> mViewRef != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span> && mViewRef.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">get</span>() != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//判断是否与View建立关联</span>    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">detachView</span>(){        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>(mViewRef != <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>){            mViewRef.clear(); <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//解除关联</span>            mViewRef = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">null</span>;        }    }}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li></ul>

通常这个View类型应该就是实现了某个特定接口的Activity或者Fragment等类型。

创建一个MVPBaseActivity基类,通过这个基类声明周期函数来控制它与Presenter 的关系。代码如下:

<code class="hljs axapta has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: "Source Code Pro", monospace;font-size:undefined; white-space: pre; border-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">abstract</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">MVPBaseActivity</span><<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">V</span>, <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">T</span> <span class="hljs-inheritance" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span></span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">BasePresenter</span><<span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">V</span>>> <span class="hljs-inheritance" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">extends</span></span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Activity</span> {</span>     <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> T mPresenter; <span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//Presenter对象</span>    @Override    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> onCreate(Bundle savedInstanceState) {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onCreate(savedInstanceState);        mPresenter = createPresenter();        mPresenter.attachView((V)<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">this</span>);    }    @Override    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> onDestroy() {        <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">super</span>.onDestroy();        mPresenter.detachView();    }    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">protected</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">abstract</span> T createPresenter();}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li></ul>

MVPBaseActivity含有两个泛型,一个是View的接口类型,一个是Presenter的具体类型。

7.参考

1. Android MVP模式浅析


PS:到这里《Android源码设计模式解析与实战》读书笔记系列到此就结束了,这本书算算看了也有近2个月了,收获真是非常大,以后也会抽时间再看几遍,温故而知新嘛!在写这个读书笔记的过程中,很感谢大家的支持,评论中都是鼓励的声音真是给我了很多的信心。最让我激动的是这本书的作者之一何红辉也给了我鼓励,在这里也是再次感谢。

前一阵看到许多的博友都有写年度总结,看了之后我也是很有感触。那我也简单总结一下:其实做Android开发到现在也有一年半了,虽然称不上是什么高手、大神。但是工作上的问题基本也可以独立解决(毕竟有Google~,毕竟就我一人)同时我对我的工作和学习态度是肯定的。

谈谈写博客的初衷:写博客的时间是我做开发基本1年的时候,说来写博客也是一个机缘巧合,因为写博客之前我会经常看一些开源的项目,就比如Github的android-open-project,那么我每次都是先进Github在搜索android-open-project(我竟然连书签都懒得保存!),类似的还有很多。。。终于有一天我觉得真麻烦(你是有多迟钝),所以就把我经常用到的这些网址写了我的第一篇博客:Android开源与干货网站汇总,之后存一个书签。尝到了这种便捷的好处,就正式开始了CSDN博客之旅。

前几天碰巧看到一位大神的博客,看了下这位大神坚持写了近10年的博客,并且每月都有高质量的文章,涉及的知识也是方方面面的。真是震撼了我,那么向榜样学习,今年继续努力,既然开始了就坚持下去。

最后奉上读书笔记的所有Demo:下载链接

0 0