阅读页mvp技术需求

需求

阅读页功能多,代码逻辑复杂,使用mvp改造解耦,利于后期维护

任务拆分

  • 删除冗余代码,封装方法,可读性优化
  • 将阅读页UI与业务逻辑分层,进行解耦
  • 结合RxJava,优化消息分发逻辑

二、业务时序图/流程图

阅读页核心业务流程图

阅读页核心业务时序图

三、MVP一期类结构设计

ReaderPagePresenter类结构:

四、MVP一期操作步骤与注意点

1.操作步骤

遵循V->P->M的顺序逐步剥离代码

  1. 修改View层继承的接口,改为内部实现,方便转移到P层
  2. 剥离打开书籍业务到P层
  3. 剥离各类handler消息到P层
  4. 剥离各类控制业务到P层
  5. 剥离P层的数据请求到M层

2.注意点

1. 广播多,放在V层还是放在P层?

有多个业务使用广播,方法很长,查看都是业务相关,故放在P层

2. Handler怎么处理?

查看剥离后的P层代码,很多成员变量都会牵扯到Handler,这个怎么处理呢?

网上大部分的代码均使用回调的方式处理数据。即V-P-M-P-V

而我们的代码,拿到Handler以后,发送msg到V层处理,即V-P-M-V.

我们的Handler较多,处理起来难度较大,仿照书架P的处理方式,在P层获取到V的handler。

3. 空安全,内存泄漏?

MVP并非无缺点,空安全和内存泄漏需要注意。及时销毁PM,断开引用

五、MVP二期类结构设计

ReaderPagePresenter类结构:

六、MVP二期工作

一期可以先将V中的逻辑转移到P层中,二期可以对大P层进行拆分

1. 拆分多个PM层

借鉴网上开源项目思路,复杂的业务需求可拆分为多个PM,即一个V持有N个P,一个P持有多个M.进一步解耦庞大的P层代码。

目前耦合有三类,解耦思路为单例设计模式

1> 书籍信息类信息:即CurBook,autoBookmark。

1.当前书籍类型类(文本书,精排书),CurBook,

此变量维护当前书籍类型,并转型为各类书籍调用各自的方法,做成单例类,减少单个变量传递,这样就可解耦到M层获取

2.当前书籍类,autoBookmark

以前是一个字段autoBookmark来回修改传递,拆分多个M可以在用到这个mark的时候,用一个单例类解耦

2> 服务器书籍信息类:即OnlineChapterHandle,LocalChapterHandle。这两个在MVP其实充当的是M

1.OnlineChapterHandle负责在线书的书籍信息,章节解析等功能

这个类是比较复杂的一个类,充当为M类。作用有4,获取书籍类型(下载时使用),获取书籍信息和章节信息(保存到OnlineBookOperator中的OnlineBook对象中,包含整本书的信息+章节信息),获取书籍购买类型(查询书籍信息是前置条件,从内部的ob对象中直接拿即可),检测章节一致性
备注:获取书籍信息和章节信息前,还获取了批量购买协议内容,都保存到了OB对象中,此处可以使用rxjava的merge合并两次请求

2.LocalChapterHandle负责精排试读书的书籍信息等

内部维护了一个handler的注册表,初始化的时候注册到这个地方。查询完成后,通过handler把查询结果发送出去
一共有两个方法,获取书籍类型(在下载时使用),获取书籍信息(查询精排限免期)

3> 书籍下载类:即OnlineProvider,这个在MVP中也是M,负责下载章节

此类维护了两个对象,书籍下载的章节,和正在预取的章节。作用有2,获取章节(下载章节),维护下载章节队列

困难点:上述三个类考虑到复用问题(新增类还是复用,复用的话handler+听书如何复用),仍有待商榷

4>非耦合类ReaderPageModel

这个类,用来负责阅读页其他网络请求,比如广告,付费等

2. rxjava引入与handler

目前数据的处理散布在N个类内,比如OnlineProvider,OnlineChapterHandle,LocalChapterHandle等,这类数据相关的类,可以考虑移动到M层。

对V的所有操作,都是P层在控制,PM之间通过回调进行。可以逐步去掉handler的使用。

rxjava的引入。rxjava流式处理较好用,可以无缝切换线程。在项目中的体现就是:子线程拿数据(M)--> 切换--> 主线程展示UI(V).我们可以把rxjava运用到P层,来处理P层中涉及到这个操作的逻辑。

七、一期改造效果

仓库地址:http://xxxx/book_cooperate_client/cooperate-baseline.git (feature_xxxx_20181122_readerpage_mvp)

删减ReaderPageActivity类 初始行数13102,抽取结束后4853,代码减少63%,包含View交互。

新增ReaderPagePresenter类 行数6170,包含书籍加载+书籍控制+付费

新增ReaderPageModel类 行数231,包含数据请求

八、三期类结构设计

二期设计因为单例设计模式,在多个阅读页同时存在的情况下,会有实例问题。比如当前在读书籍Mark, 打开新阅读页的时候,会把老阅读页的单例Mark更新,导致老阅读页的书籍信息被修改。
所以对二期的设计进行了下修改,解决多阅读页问题。同时引入代理模式和观察者模式,解决多P协作,多P数据统一等问题。

ReaderPagePresenter类结构如下:

以下:ReaderPageActivity简称V,ReadpagePresenterImpl简称代理P,CommonPresenter等简称业务P,ReaderPageModel等简称M

代理模式

V持有代理P的引用,通过调用代理P的方法,完成P层逻辑。
引入代理P,一共有两个作用:
1.隐藏业务P的业务实现,可以进行后续业务扩展,不用更改V层代码
2.解决多P协作问题,如遇到多P才能完成的业务,可以写在代理P中,不用在V中进行周转

观察者模式

代理P是被观察者,业务P是观察者。代理P中持有一个观察者队列,ArrayList,通过业务P的observe(ReadpageObservable observable)方法,注册到观察者队列中。
当业务P中,某些需要同步的对象(如Mark)更新时,可以通知被观察者,更新观察者队列,做到了多P数据统一。

备注:因为业务涉及到多阅读页形式,无法使用EventBus,RxBus之类类库添加全局观察者,所以就实现了一个有局限性的观察者模式。如果以后无此类多阅读页需求,可以修改为三方类库实现,业务逻辑更加清晰一些。

疑问

通过拆分成不同的Iview,和Ipresenter.可以合理的规划业务边界,编写业务代码。但是也会引入两个新的问题
1.如果业务边界不清晰,怎么划分。即如何避免代理P增大的问题
2.如果业务变动频繁,怎么快速完成,改动量最小。