APP架子迁移指南(二)
接上一篇,这一篇开始用android来解释MVP概念、八股式的架子结构和命名规范。我在准备这篇文章的时候还看到不少在MVP基础上衍生的架子思路,底子是MVP没错,但命名有区别、复杂度变了、架子也用到了module拆分而不单纯用包进行拆分,所以接下来会基于googlesamples推荐的命名、架子结构来重构我的约炮APP,我会pull个新的branch来对应各个章节,接下来会从我认为合适的难度适中、实用的方向继续重构。
搭架子切忌过度!。搭架子就是为了隔离代码,该干嘛干嘛(说白了就是就是读数据的放一个文件夹,界面放一个文件夹)。现在流行的“domain”、“repository”、“Service”等命名,你若.net出家肯定会心一笑:“又TM来装逼了”--这些命名和早年的"module"、“Biz”、“helper”命名如出一辙(你可以去苏菲论坛看看大神几年前写的,至今也非常好用的那套.NET通用库)。
当你觉得看不明白的时候就该收手了。还是再强调一次这种观点,比如我为了准备文章看了这篇clean-architecture和这篇mosby。一脸的懵逼,并不是因为鸟语差,我可以毫不费劲的看完,而是因为我在看的时候脑海里一直深深的感觉“我有必要这样搞么?”,后面我突然明白,广点通2毛钱收入不是因为架子差,而是因为crash太多,哈哈哈。看不明白了还要继续装逼,就像刚开炉罩就寻思怎么承载千万级并发一样傻逼。你只要记住哪个文件夹放什么代码文件,干些啥这种层度,就足够受用了。
顺便预告一下,在后面讲IOS端,我会来讲讲MVC模式怎么结合搭架子的思路,正好也为出文章给点动力马拉松一次把一直想做的ios端做了(因为到现在用广点通收入才2毛钱不想弄,哈哈哈)。IOS现在炒MVVM火的很,关于MVVM模式我是很拒绝的,在早年做silverlight的时候用过,觉得太麻烦了(这也是我想提的观点,不要过度),数据绑定这件事,逻辑简单还好,要做很多其他工作,你光是写架子的基础代码就够折腾了。在后面讲PHP端(Laravel)的时候,我就聊聊文件夹该怎么放,文件名该怎么取显得很专业,以伟大又优雅的laravel框架来总结下全篇。
值得看的文章和项目
自从微博关注了很多大神和几个开发博客之后,各种技术文章炸屏有没有..尤其是像我这种好奇心胜的,关注的方向不少,看得很压抑啊。这里我先给几个我认为你应该学习的文章和项目,比我写的好、写的生动(打星标是指我认为的阅读友好度)。
- Google原味mvp实践 ★★★★
- Android:你是如何把Activity写的如此“万能”的 ★★★★
- Android:“万能”Activity重构篇 ★★★★★
- MVP+Dagger2+Retrofit实现更清晰的架构★★★
- 从零开始的Android新项目6 - Repository层(下) Realm、缓存、异常处理★★★★(这个系列强烈推荐看完,大神还放了一个没混淆没加固已上线的apk让你逆向,感动)
- Architecting Android…The clean way? ★★★
- Architecting Android…The evolution ★★★
- the-clean-architecture ★★★
- Model-View-Presenter library for android ★★★
项目,要发现新的会编辑添加:
白话MVP模式
在学习架子的过程中,各种翻译差异和理解误差造成了诸多困扰,什么是“业务逻辑”?“领域层”?“数据映射层”?很烦有没有,尤其是早期网上的文章作者都是大神,觉得人民群众都是学霸,也不给点例子就用无比晦涩的描述带过去了。好在现在很多大神发现了这一点,开始用生动轻巧的语言来描述这些概念。前面推荐的文章相信你也吸收的差不多了,我就结合实际细节再白话一遍MVP模式(我没老司机带,理解偏差请纠错)。
你跟我一样吗
以前你是不是在一个叫data或者bean或者model的包里面放了很多数据实体?如“UserBean”,定义用户的名字、性别字段,添加get/set方法,持久化(Parcelable),用gson或orm的话还会加上@SerializedName、@Table之类的注入。
接下来你会把网络访问哪个网址取得用户数据的代码写在一个叫Manager或者Api或者Network的包里面,json数据从网络读取并通过gson转换你会写在listview所在的fragment或者activity文件中(比如叫loadNewData)。
如果你是有心之人,那么你还会写sqlite操作的逻辑并放在一个叫dao或者db的包,把数据写入到sqlite中去存起来。每次从数据库读或者写,会用asyncTask执行(可能你会新建一个叫task的包或者listview所在的fragment或者activity文件中,当时我图省事没弄)
如果你拿着票子想让功能更健壮,上传数据的代码写在service里面,那就会添加一个service包;需要添加notification,那么你会新建一个叫boardcase的包和notification的包,一个存广播、一个存notificaition;图片处理、时间字符串处理等功能写在一个叫utils的包里面;第三方轮子View控件写在一个叫widget的包里面。
发现啥问题没
写代码讲究个“逻辑分离”,简单讲读写数据的凑一堆;读写网络的凑一堆;界面交互(包括数据显示)凑一堆,这样的好处就是维护起来方便,易于扩展(项目小没这种体验?那在硬盘里面,代码放一个文件夹,ui设计素材放一个文件夹,文档放一个文件夹,要用的时候直接打开哪个文件夹心里了如指掌,这样理解了吧)。
那其他文章里总是提“业务逻辑”、“功能逻辑”,怎么区分呢。以登录功能来举例:
- 你用手机号或者微信登录,验证格式或者通过微信获取token和userinfo,都是为了实现登录功能,但是方法不同,完成这些操作你可以调用umeng或者第三方的原生SDK,为这些写代码,算业务逻辑。
- 把用户注册信息发送到服务器保存,并接收服务器反馈(比如保持用户登录状态的token),存进preference或者数据库,这些代码算业务逻辑。
- 怎么把数据就存入preference或者数据库,无非就是增删改查,那么这部分代码就算功能逻辑。
为实现具体业务功能写的代码,叫做“业务逻辑”。登录的时候获取人员位置、短信验证码都算。并且,你存入数据库之前需要修改服务器反馈的数据(比如从微信拿用户性别是m、f,你想存为0、1表示),这种代码也算入业务逻辑中去的。在各种架子中,“domain”、“service”、“repository”里面干的全是这些事。
为实现可复用的功能函数代码,叫做“功能逻辑”。不管业务是登录还是注销,想访问网络或者读取数据库,最后都会调用平台的方法吧,那么如何调用平台方法的代码,就是功能逻辑。如访问网络,不管是想post登录的网址或者post注销的网址,都会用到调用android网络访问的代码;如增删改查,不管你是preference或者sqlite,最后都会调用android方法执行;如数组转换、临时文件管理、图片压缩、检查字符串是否为空、md5加密、时间格式化等等,你可以写一个叫utils的包保存这些代码。boardcase、receiver、sevice、notifacation都应该以包为单位分离。
按照这样的思路进行细分,那么很明显的,上一节中目前我app的框架虽然包的结果清晰,但是读起来是一团糟的,而且确实是“读”起来一团糟,比如index的fragment代码行数过高,我不得不简单的把代码“挪”到一个BaseIndexFragment中,简单的把数据读取和界面交互的代码分开便于阅读。MVP模式就很好的解决了我这样不专业的分离方案,提供了标准模板,什么代码该放哪就放哪。
Model层不再只是放数据实体了
按照MVP的说法,Model层是要把所有和数据有关的代码都放在这里的,也就是说定义数据实体、网络读取json并转换为数据数组、数据库读写数据这些代码全部都写在这里。Presenter层想获取数据,那就一定是已经处理好不需要再处理的数据了,怎么获取数据(从网络或者数据库)的、数据怎么转换的,通通都不用管。
在项目里面体现在什么地方呢,listview所在的fragment中,从网络获取json并转换为数据数组操作的代码从原来的loadNewData函数中移到了Model层的XXRemoteDataSource文件中去。从数据库获取数据的asyncTask代码移到了XXXLocalDatasource中去,判断显示本地数据还是网络数据的代码移到了XXXDataRepository中去。这样一来,fragment中的代码一下就清爽了很多,是不是感觉很好?我们继续看Presenter。
Presenter层就像电梯
数据读取前、读取后该干嘛?点击登录按钮后服务器返回登录状态该跳转首页还是提示密码错误?这些事都是Presenter干的:把Model层准备好的数据显示在界面上,有交互时提醒Model层数据需要更新。就像一台电梯,人进来,去按摩到3楼,去ktv到4楼,去开房到5楼。进来的是男是女你不需要关心,出去干嘛你也不需要关心。
在Presenter层里面会大量用到接口(interface)定义行为。以前是不用接口的,第一嫌麻烦,第二人家是团队这样方便测试,我只搞暴力测试没那么讲究。现在还是老老实实用吧,如果不习惯你可以先实现功能,再重构接口,或者把常用的增删改查先复制进去用,慢慢习惯。
这时你可能会有一定的困惑,如果有涉及诸如IM通信、百度LBS定位这样功能逻辑的代码,该放哪里?这些代码可能需要依赖上下文(就是构造函数要context或者activity,你肯定见过),我认为应该放到utils包里面。传递参数、状态成功或失败该怎么处理的代码才放到Presenter中去。也就是说和界面有关的代码才放在Presenter中,其他按业务逻辑或功能逻辑能归类的尽量重构。
View层不是指的控件
这里的View是思想,不是具体指按钮控件或者列表。你可以脑补一个画面:View和Presenter在床(fragment或activity)上搞基,一会Viwe在上面,一会Presenter在上面。
从一个简单的新闻列表,到复杂点有轮播banner、多级下拉菜单,都可以在View层中表达控件的状态和交互动作,通知presenter这个控件被点击要执行啥操作了,或者prenseter通知这个控件应该被隐藏或显示了。View层在MVP结构中,反而是写代码量较轻的一块,只要Model和Presenter写好了,明天在github上看到一个更酷炫的轮子或者想添加一个动画效果,都是很清爽的事。
聊聊别的
我相信你已经看过我推荐的那几篇文章了,那么我这很白话的把mvp模式讲了一遍,用很多平时会用上的细节来解释,应该对概念的把握比较深刻了。那么在我把我的项目更新完成写下一篇之前,我们再聊聊别的。
RxJava和handler到底该用哪一个?
你们是不是也喜欢把自定义的handler写在fragment文件里面?或者asyncTask之类的,或者adapter。功能简单点的倒是问题不大,这样写又快又省事,阅读性也可以。但要是你遇上登录功能,首先通过umeng获取用户信息,成功了再从服务器获取七牛的上传token,然后传图片,图片上传成功把头像地址和用户信息post到数据库里面,成功了从服务器获取token存app数据库。像这样“圆环套圆环”的业务代码,画时序图都挺麻烦,用handler可以写,但是隔一段时间再阅读起来就很不开心了。
RxJava的流编程思路就可以很好的解决这种问题,虽然代码量并不见得变少,但却是可以让阅读起来开心一点。至于网络模块你用async-http-client或者okhttp或者volley,或者最近很火的retrofit2,这就看自己了吧。我一直在用async-http-client,够老的轮子了吧,也没觉得有什么不妥的(可能我的网络访问规则太简单了)。另外你想当技术网红的话,RxJava+Retrofit2是必备神器,但要.net出家的人,还是呵呵吧,没必要炒到这种高度。
我的文章肯定有问题,求指正
我没老司机带,全靠github和逆向工程学习。肯定会有问题,还请发现了指正,非常感谢!qq群533838427
APP架子迁移指南(二)的更多相关文章
- APP架子迁移指南(一)
搭架子是脑垂体在放烟花 俗话说吃多少饭,走多少路,上学的时候捧着<设计模式>就想睡觉,现在轮子看得多了,自然有心领神会之感.搭架子就像谈哲学,如高山流水,遇弯则急.遇潭则深.我印象最深的是 ...
- APP架子迁移指南(三)
在完成上一篇之后,断断续续的开始重构我的Android项目代码,现在终于完成了.在重构期间又仔细阅读了一些开源项目的源码及文章,并询问了一些大神思路,按照理解自己完成了MVP结构的重构,与google ...
- App架构师实践指南二之App开发工具
App架构师实践指南二之App开发工具 1.Android Studio 2.编译调试---条件断点.右键单击断点,在弹出的窗口中输入Condition条件.---日志断点.右键单击断点,在弹 ...
- Spring Cloud Alibaba迁移指南(二):零代码替换 Eureka
自 Spring Cloud 官方宣布 Spring Cloud Netflix 进入维护状态后,我们开始制作<Spring Cloud Alibaba迁移指南>系列文章,向开发者提供更多 ...
- Magento网站迁移指南
"Magento网站迁移指南":关键词:magento 网站 迁移 指南 上周五,为mkt同事迁移了一个从本机到godaddy的magento系统. 中间出了不少状况, 现在写个迁 ...
- App Store生存指南
资格获取 如果已经有App Store开发帐号请跳过此节. App Store的资格获取其实一直以来都不算难,和其它事情一样,需要的只是耐心.现在苹果对申请者的文书手续要求已经比几年前简化多了 ...
- 精华阅读第 12 期 | 最新 App Store 审核指南与10大被拒理由?
很多时候,我们对技术的追求是没有止境的,我们需要不断的学习,进步,再学习,再进步!本文系移动精英开发俱乐部的第12期文章推荐阅读整理,其中涉及到了 Android 数据库框架,架构设计中的循环引用,同 ...
- DCloud-HTML5+:5+ App开发入门指南
ylbtech-DCloud-HTML5+:5+ App开发入门指南 1.返回顶部 1. 5+ App开发入门指南 App App入门 HTML5 Plus应用概述 HTML5 Plus移动App,简 ...
- 【转】总结:2015这一年App Store审核指南都有哪些变化
本文针对此前版本的<App Store审核指南>进行了更新,并标注了2015年苹果对<App Store审核指南>进行的一些调整. App Store Review Guide ...
随机推荐
- WebApi深入学习--概述+路由查找
如何创建Controller这里就不说了,只写一些可能是高阶知识的内容 关于WebApi的官方介绍及示例 http://www.asp.net/web-api/ 1.跨域 Asp.NET有专门的跨域扩 ...
- 【函数】Oracle函数系列(2)--数学函数及日期函数
[函数]Oracle函数系列(2)--数学函数及日期函数 1 BLOG文档结构图 2 前言部分 2.1 导读和注意事项 各位技术爱好者,看完本文后,你可以掌握如下的技能,也可以学到一些其它你所不 ...
- 【转】RHadoop实践系列之二:RHadoop安装与使用
RHadoop实践系列之二:RHadoop安装与使用 RHadoop实践系列文章,包含了R语言与Hadoop结合进行海量数据分析.Hadoop主要用来存储海量数据,R语言完成MapReduce 算法, ...
- 大型网站的 HTTPS 实践(1):HTTPS 协议和原理
转自:http://op.baidu.com/2015/04/https-s01a01/ 1 前言 百度已经于近日上线了全站 HTTPS 的安全搜索,默认会将 HTTP 请求跳转成 HTTPS.本文重 ...
- MySQL运行状态show status中文详解(转)
要查看MySQL运行状态,要优化MySQL运行效率都少不了要运行show status查看各种状态,下面是参考官方文档及网上资料整理出来的中文详细解释: 状态名 作用域 详细解释 Aborted_cl ...
- Java api 入门教程 之 JAVA的IO处理
IO是输入和输出的简称,在实际的使用时,输入和输出是有方向的.就像现实中两个人之间借钱一样,例如A借钱给B,相对于A来说是借出,而相对于B来说则是借入.所以在程序中提到输入和输出时,也需要区分清楚是相 ...
- Java的String.valueOf 转换 与、空串+类型变量转换与封装类(Integer)的toString方式转换比较。
1.空串+类型变量方式转换 int i=20; String s=""+i; 这种方式实际上经过了两个步骤,首先进行了i.ToString()把 i 转换为 字符串,然后再进行加法 ...
- Linux学习--------一
用户不能直接操作Kemel,所以需要通过Shell来操作Kemel(内核) Shell 分为CLI与GUI两种 CLI:Command Line Interface GUI:Graphical Use ...
- shell脚本实现随机筛选
#!/bin/bash name=(val1 val2 val3 val4 ...) a=$() #以时间产生随机数向39取余得到0~38的值
- WinCE下GPRS自动拨号软件(GPRS AutoDial)
之前在WinCE下调试USB的3G Modem时,写过一个拨号助手RASManager,基本能用.后来车机卖到俄罗斯去,客户老M提供了一个更好的GPRS自动拨号软件GPRS AutoDial,功能完善 ...