在完成上一篇之后,断断续续的开始重构我的Android项目代码,现在终于完成了。在重构期间又仔细阅读了一些开源项目的源码及文章,并询问了一些大神思路,按照理解自己完成了MVP结构的重构,与google samples项目的大致一致,但没有完全照搬。本文侧重一些重构过程中思考的问题,,具体的代码可以在Github查看,本文的源码为branch1.1,重构前的是master,最好对比看看重构的区别。

对多重callback逻辑的思考

大量的文章都只介绍读取一次网络然后用一个监听接口处理访问状态,这种是最常见的网络访问规则,如下图所示:

用监听的方式获取访问结果

但实际生产环境哪里仅仅是简单的为列表获取数据而已,要是完成业务逻辑需要多次网络访问呢?来看一次登录过程,我将每一个步骤都截图如下:

基于UMENG SDK访问微信获取token,成功则执行下一步

1.基于UMENG SDK访问微信获取token,成功执行下一步。

基于UMENG SDK获取用户资料(头像、昵称),成功则执行下一步

2.基于UMENG SDK获取用户资料(头像、昵称),成功则执行下一步。

将数据发送服务器进行登录验证

3.将数据发送服务器进行登录验证,完成登录业务。

说起来逻辑很简单,但是从代码实现角度就不够优雅了。在OnSuccess中执行下一步动作,如果不写在另一个方法函数中的话,那么看起来就是一层套一层的结构,可读性很差,我考虑的有几种技巧来尽量提高可读性:

1.添加充足的注释
2.用Handler(之前我是这么做的)
3.用EventBus
4.用RxJava

在这次重构中,我放弃了Handler的方式,如果注释充足,Handler其实并没有提高可读性,而且从MVP架构的角度来思考,之前Handler的编写并没有将View逻辑与Presenter的逻辑分离,全部放在了Handler中,应该尽量避免。

放弃handler了

EventBus在重构中也未使用,因为EventBus的订阅模式更适合一个操作需要通知多个处理的情况(比如收到新消息),否则与监听接口相比阅读性。并未提升太大。

我倾向使用RxJava,虽然准备在下一次重构中才使用,但已经阅读了不少的文章。在注释充足的情况下,现在callback套callback的方式已经能够阅读,但不可忽略的线程安全和内存溢出问题,可以利用RxJava很好的解决(这也是放弃handler的一个原因,你真以为实际生产中new一个出来就可以不管了么..)感兴趣的朋友可以等我的下一个branch,我会来说说我的感受。

MVP重构要一贯坚持到底么?

在google sample的todo例子中,要想从列表页打开新建页,需要点击浮动按钮,这样一个简单的在onClick中搞定的事情,被拆分为三部分,我认为实际有点过了。看看下面的代码:

菜单栏按钮点击事件

如果点击消息列表的按钮,需要首先清除通知badage,然后清除Preference的未读数量,然后进行页面跳转。按照MVP架构思路,清除通知badage应该在View中实现(也就是在Activity里多一个方法);清除未读数量应该在Model的Local中(也就是专门负责本地数据操作的类);跳转应该在View中实现(Activity里面还要多写一个方法)。

那么其实在OnClick中3行代码搞定的时候,你要额外多谢那么多支撑框架的代码,有必要么?再看看Model层的一个例子:

在google sample的todo例子中,网络访问与本地访问分离,一条数据是从本地读取还是从网络读取的逻辑判断,是写在Repository中的。如此毫无问题,思路非常清晰。但如果不存在本地化的需要,可以把代码直接写在Repository中么?(其实可以吧...反正也不影响阅读)

重构之后层次很干净

todo例子中,网络与本地的操作是保持一致的(基础的增删改查),用接口来定义了方法。在实际生产中(比如登录过程),本地与网络的操作差别比较大,因此应该根据实际判断到底是否需要用接口定义方法。

todo例子中,用到了很多Ioc的思想,如果你不用DI库如dagger2或者要做单元测试,可以考虑简化一下,正如我实例化Presenter的代码一样。

重构之后,数据的成员变量该放哪去了?

详情页,需要一个描述商品数据的成员变量;其他用户的介绍页,需要一个描述用户资料数据的成员变量;列表,需要一个数组来保存数据,一个int来保存现在是第几页了。那么MVP结构重构之后,这些变量是不应该仍然定义在Activity中的,那么这些数据应该放在MVP的那一层呢?

安装todo例子中的介绍,包含2方面的重构。首先,数据变量应该写在P层中;其次,对ListView这种包含adapter的控件,数组操作应该写到Adapter中。

Presenter的构造器

以我一个Presenter为例,由于不考虑注入,因此Repository就直接在构造器中自己实例化了。当前列表为第几页的mPage变量,是放在Presenter中了的,每次获取数据后都直接将数据传入View中,再在View中调用Adapter的自定义方法来更新列表(详情见代码)。

ListView的数据操作都在Adapter中定义

在Adapter中,自定义了remove、replace、addAll、getItem等几种常用的数据操作函数,不够再加就是了。自定义这些方法的时候,尤其要注意你是否自己实现了header-view的功能,如果实现了,那么就要计算一下实际的位置(其实特简单,一句话的事)。

后话

其实本来想按照重构后MVP长什么样的思路来写这文章,但是在重构的过程中不断的阅读todo例子的代码,觉得把握住几个关键点,重构的思路就会非常清晰:

1.各种数据操作都要放到M层中去;
2.V层中只需要定义方法的时候把数据设为参数,不管数据从哪来,到哪去;
3.P层负责告诉M层要读哪些数据(get)或者要向服务器发送什么数据(set),根据数据访问情况(成功或失败),将数据传给V层;

这一篇更多的是想把自己重构过程中的一些思考记下来和大家一起讨论,本篇不是这次重构的终点,下一篇我会继续聊如何从这一步继续跨到RxJava函数式编程的范围去。每一次重构是怎么变化的,代码也给了,思路也给了,项目也是应用商店上线的产品(虽然没有钱推广用的人不多),这个系列应该比其他文章的玩具代码参考价值更高吧。如有感兴趣的大神指点一二,或者也在前行的朋友一起来讨论下,那我也觉得深夜码字没有白费了。

APP架子迁移指南(三)的更多相关文章

  1. APP架子迁移指南(一)

    搭架子是脑垂体在放烟花 俗话说吃多少饭,走多少路,上学的时候捧着<设计模式>就想睡觉,现在轮子看得多了,自然有心领神会之感.搭架子就像谈哲学,如高山流水,遇弯则急.遇潭则深.我印象最深的是 ...

  2. APP架子迁移指南(二)

    接上一篇,这一篇开始用android来解释MVP概念.八股式的架子结构和命名规范.我在准备这篇文章的时候还看到不少在MVP基础上衍生的架子思路,底子是MVP没错,但命名有区别.复杂度变了.架子也用到了 ...

  3. App架构师实践指南三之基础组件

    App架构师实践指南三之基础组件 1.基础组件库随着时间的增长,代码量的逐渐积累,新旧项目之间有太多可以服用的代码.下面是整理的公共代码库. 2.关于加密密钥的保护以及网络传输安全是移动应用安全最关键 ...

  4. App Store生存指南

    资格获取   如果已经有App Store开发帐号请跳过此节.   App Store的资格获取其实一直以来都不算难,和其它事情一样,需要的只是耐心.现在苹果对申请者的文书手续要求已经比几年前简化多了 ...

  5. Magento网站迁移指南

    "Magento网站迁移指南":关键词:magento 网站 迁移 指南 上周五,为mkt同事迁移了一个从本机到godaddy的magento系统. 中间出了不少状况, 现在写个迁 ...

  6. Oracle JDK迁移指南

    Oracle JDK迁移指南 https://docs.oracle.com/en/java/javase/11/migrate/index.html#JSMIG-GUID-C25E2B1D-6C24 ...

  7. 精华阅读第 12 期 | 最新 App Store 审核指南与10大被拒理由?

    很多时候,我们对技术的追求是没有止境的,我们需要不断的学习,进步,再学习,再进步!本文系移动精英开发俱乐部的第12期文章推荐阅读整理,其中涉及到了 Android 数据库框架,架构设计中的循环引用,同 ...

  8. 最新App Store审核指南与10大被拒理由

    最近,苹果在官网给出了截至2015年2月份应用被拒绝的十大理由,其中50%以上的应用被拒绝都是因为这10个原因,其中7个理由和2014年相同,其中排名前三的原因分别是:需要补充更多信息.存在明显的bu ...

  9. ASP.NET Core 2.x 到 3.1 迁移指南

    一.前言 今日(2019/12/4).NET Core 3.1 正式发布了,ASP.NET Core 3.1 随之发布,这次 3.0 到 3.1经过了两个月的短周期,并没有增加重大的功能,主要是对 3 ...

随机推荐

  1. HBase应用开发回顾与总结系列之二:RowKey行键设计规范

    2. RowKey行键设计规范 2.1. RowKey四大特性 2.1.1 字符串类型 虽然行键在HBase中是以byte[]字节数组的形式存储的,但是建议在系统开发过程中将其数据类型设置为Strin ...

  2. IE10 11的css hack

    一.@media -ms-high-contrast @media screen and (-ms-high-contrast: active), (-ms-high-contrast: none){ ...

  3. linux-3.14.13 看到mpls gso支持

    在net/Kconfig source "net/mpls/Kconfig"

  4. arm汇编进入C函数分析,C函数压栈,出栈,传参,返回值

    环境及代码介绍 环境和源码 由于有时候要透彻的理解C里面的一些细节问题,所有有必要看看汇编,首先这一切的开始就是从汇编代码进入C的main函数过程.这里不使用编译器自动生成的这部分汇编代码,因为编译器 ...

  5. Eclipse RCP实用小技巧

    1.插件Plugin工程的build.properties中增加一行,javacDefaultEncoding.. = UTF 2.插件Plugin工程的Runtime的classpath中加入.

  6. 移动端rem单位用法[转]

    标签: 1.rem(font size of the root element)是指相对于根元素的字体大小的单位,em(font size of the element)是指相对于父元素的字体大小的单 ...

  7. git忽略某些文件

    通常项目中编译过程产生的文件或者编译器生成的临时备份文件,不需要通过Git系统去追逐(track). 在项目顶层工作目录中添加一个叫".gitignore"的文件,来告诉Git系统 ...

  8. NET Core中实现一个Token base的身份认证

    NET Core中实现一个Token base的身份认证 注:本文提到的代码示例下载地址> How to achieve a bearer token authentication and au ...

  9. 怎样在ZBrush中快速绘制人体躯干

    之前我们对人体骨点的雕刻,了解了人体骨骼比例结构特征.今天的ZBrush教程将通过ZBrush®遮罩显示的特点对模型的人体躯干进行细致雕刻.文章内容仅以fisker老师讲述为例,您也可以按照自己的想法 ...

  10. Ubuntu 14.04 下 Chromium 出现 未安装Adobe Flash Player 问题解决

    Ubuntu 14.04 中,其他浏览器在安装Adobe Flash插件后可以播放视频及音乐,但是Chromium浏览器则会提示缺少Adobe Flash 插件. 原因:之前Chromium使用Net ...