TODO-MVP源码解析

TODO-MVP源码解析

Google Samples上有很多优先的app代码范例,最近在总结设计模式时,看到书中提到了Smalltalk的MVC模式,这个缩写好像经常能在网上见到,还往往还MVP一起出现。我以前也稍微了解过一下,但是网上对MVC运行原理的解释有很多版本,被弄蒙后也没看下去了。恰好又偶然在Google Samples上看到了这个用MVP模式写的todo应用,于是想尝试通过代码的角度来了解MVC和MVP的真面目


MVC

根据Wikipedia上的解释,MVC的模型是这样的

  • Model封装着业务逻辑,算法等核心函数,并且对数据有直接的访问权限,因此Model不依赖于Controller和View,它只要好好处理请求和返回数据就行了
  • View负责数据的格式化显示。View没有程序上的逻辑,它显示的数据都来自Model,因此它需要在Model上进行注册,当接收到Model发送的更新通知后,View向Model获得数据并格式化显示(观察者模式)
  • Controller封装了UI响应机制,用户直接对Controller进行交互,Controller得到响应后向Model进行请求

MVC被诟病的地方是View和Model之间的强耦合,View可以访问Model,因此是依赖Model的,这使得View的更换比较麻烦,再加上因为可以访问Model使得View本身也超越了显示的义务,很容易在里面混入重要的业务逻辑

MVP

MVP的提出就是想要把View和Model分离开来:

这次用户直接和View交互,View把响应提交给Presenter,Presenter处理后向Model请求数据。Model通过观察者模式向Presenter通知,而不是View,最后Presenter通过View的接口更新显示。
整个过程中View和Model没有直接的接触,通过中间者Presenter完成消息和数据的转发

Android

把MVC和Android中的结构结合来看,可以把layout看作View,把Activity看作Controller。
因为layout只是对界面的布局和空间属性的进行初步的配置,因此功能非常有限,也不能和Model进行接触。这样一来,很容易就在Activity中写入大量的代码,包括监听控件,接触Model,更新界面,越来越臃肿,当工程大到一定程度后,就会发现此时Activity已经很难进行维护了


TODO-MVP

现在进入正题
先来看看作者提供的模式图:

可以看到这里的View指的是Fragment,一是因为Google推荐使用Fragment而不是Activity来显示内容,二是Fragment作为View既能解决layout作为View导致的鸡肋,也不会使Activity功能太过膨胀(这里Activity是一个总体的Controller,让Fragment和Presenter进行连接)
左边那块就是Model了,Presenter先到内存中的缓存进行查询,如果没有,才到本地数据源或者远程数据源请求

再看看源代码的总体情况:

每个包对应一个界面,可以看到,包中的结构基本就是一个Activity,一个Contract,一个Fragment,一个Presenter。作者说明中说到:

  • Contract定义了View和Presenter接口
  • Activity创建了View和Presenter
  • Fragment实现了View接口
  • Presenter实现了Presenter接口

先来看tasks包,这是应用的主活动,也就是显示任务列表的界面

TaskActivity

onCreate中先对Toolbar,DrawerLayout和NavigationView进行了初始化,然后是:

通过FragmentManager得到布局中id为contentFrame的Fragment的实例,因为在onCreate的时候Fragment尚未创建,所以是null的。
接着通过TasksFragment的静态方法newInstance()来获得一个实例(既然希望这样产生实例,那为什么TasksFragment的构造器还是public的呢?奇怪)
ActivityUtil类中只有一个静态方法addFragmentToActivity,其实就是把一个Fragment add到活动中对应的地方去


接着就是创建一个Presenter了,可以看到参数有两个,一个是TasksRepository,任务贮藏室,顾名思义,应该就是提供任务信息的Model了,另一个就是对应View的TasksFragment。就这样,Presenter把Modle和View连接了起来

看一下TasksPresenter的构造函数:

检查两个参数是否为null后保存应用,然后tasksView设置自己为Presenter。第二个参数不是TasksPresenter类的吗,怎么在这里变成了View类的呢?前面说到了,每个包中有四个成分,其中一个就是定义了View和Presenter接口的Contract类,由于Fragment在这里扮演的就是view的角色,所以可以看到

TasksFragment就是继承了TasksContract的View接口

上面就是MVP三部分的创建和连接,那么它们是怎么运行并相互联系的呢

Fragment显示就绪后,就进入到onResume函数中:

这里调用了对应的TasksPresenter的start()方法

start()方法:

继续追踪:

这里会继续调用loadTasks的一个重载版本,而且出现了mFirstLoad这么一个成员(字面意义上应该是指是否第一次加载)
继续往下看:

注解中写了,forceUpdate表示需要从数据源TasksDataSource中获得刷新(如果为false呢?从缓存?),第二个参数表示是否需要显示加载控件
如果forceUpdate为真,那么TasksRepository会调用refreshTasks(),里面其实就是把一个成员变量mCacheIsDirty设为false,缓存既然脏了就需要从数据源更新了嘛
下面的Espresso好像是Google提供的测试工具,以后了解一下
最后是TasksRepository对象调用getTasks函数,其中传入了一个匿名内部类,实现了LoadTasksCallback接口,看来就是数据获得后的回调接口

接下来看看TasksRepository的getTasks函数:

如果缓存不为空,且mCacheIsDirty为false,就直接把缓存中存储的数据传给回调函数。当然之前已经把mCacheIsDirty设为true了

如果缓存已经脏了,向网络数据源请求,否则搜索本地存储,还是没有的话再向网络数据源要

再往下看就超出分析MVP的范围了,所以这里就不写了,而且它这里的远程数据源类名叫FakeTasksRemoteDataSource,其实就是模拟的嘛,最后返回的是一个空的队列

获得数据后,经过更新缓存等操作后,就调用回调接口的onTasksLoad方法,这个传入的这个匿名内部类的onTasksLoad方法里面在把Task对象加入到队列中后,最后调用processTasks方法:

Task队列不为空,于是调用TasksFragment的showTasks函数,当然这里面就是把队列传给列表适配器然后更新控件了

于是整个更新流程就完成了,总结一下:
TasksFragment作为V,负责的就是接收数据,更新界面
TasksRepository作为M,负责和数据的直接接触,这里要注意的是TasksRepository没有保存V和P的引用,说明M是完全独立的
TasksPresenter作为P,V向它请求数据,P分析后向M请求数据,回调函数返回数据后,让V更新界面
结构大概是这样的:

跟作者给出的模式图相比,可以看出是很相似的

应用了MVP模式后,虽然比平时多出了很多类,这也是使用设计模式的一个通病,但是正因为把代码进行拆分,整体清晰了很多,可读性大大增加,模块化的设计也使得日后的代码维护和替换变得更加容易(在V和P中间加入适配器模式就更上一层楼了)
只能说真不愧是Google的Sample啊

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值