一种更清晰的Android架构(转)
一种更清晰的Android架构
一种更清晰的Android架构
过去几个月以来,通过在Tuenti网站上与@pedro_g_s和@flipper83(安卓开发两位大牛)进行友好讨论之后,我决定写这篇关于架构安卓应用的文章。
我写这篇文章的目的是想把我在过去几个月体悟到的小方法以及在调查和应用中学到的有用的东西分享给大家。
入门指南
大家都知道要写一款精品软件是有难度且很复杂的:不仅要满足特定要求,而且软件还必须具有稳健性,可维护、可测试性强,并且能够灵活适应各种发展与变化。这时候,“清晰架构”就应运而生了,这一架构在开发任何软件应用的时候用起来非常顺手。
这个思路很简单:简洁架构 意味着产品系统中遵循一系列的习惯原则:
- 框架独立性
- 可测试
- UI独立性
- 数据库独立性
- 任何外部代理模块的独立性

我们并不要求一定要用四环结构(如图所示),这只是一个示例图解,但是要考虑的是依赖项规则:源码依赖项只能向内指向,内环里的所有项不能了解外环所发生的东西。
以下是更好地理解和熟悉本方法的一些相关词汇:
- Entities:是指一款应用的业务对象
- Use cases:是指结合数据流和实体中的用例,也称为Interactor
- Interface Adapters: 这一组适配器,是负责以最合理的格式转换用例(use cases)和实体(entities)之间的数据,表现层(Presenters )和控制层(Controllers ),就属于这一块的。
- Frameworks and Drivers: 这里是所有具体的实现了:比如:UI,工具类,基础框架,等等。
想要更具体,更生动丰富的解释,可以参考这篇文章或者这个视频。
场景
我会设置一个简单的场景来开始:创建一个简单的小app,app中显示从云端获取的一个朋友或用户列表。当点击其中任何一个时,会打开一个新的窗口,显示该用户的详细信息。这里我放了一段视频,大家看看这个视频 (需FQ)大概就可以对我所描述的东西了解个大概了。
Android应用架构
这一对象遵循关注分离原则,也就是通过业务规则让内环操作对外环事物一无所知,这样一来,在测试时它们就不会依赖任何的外部元素了。
要达到这个目的,我的建议就是把一个项目分成三个层次,每个层次拥有自己的目的并且各自独立于堆放运作。
值得一提的是,每一层次使用其自有的数据模型以达到独立性的目的(大家可以看到,在代码中需要一个数据映射器来完成数据转换。如果你不想把你的模型和整个应用交叉使用,这是你要付出的代价)。
以下是图解,大家感受下:

注:
我并没有使用任何的外部库(除了用于json数据句法分析的gson和用于测试的junit, mockito,
robolectric和espresso以外)。原因是它可以使这个示例更清晰。总之,在存储磁盘数据时,记得加上ORM、依赖注入框架或者你熟悉的任
何工具或库,这些都会带来很大帮助。(记住:重复制造轮子可不是明智的选择)
表现层 (Presentation Layer)
表
现层在此,表现的是与视图和动画相关的逻辑。这里仅用了一个Model View
Presenter(下文简称MVP),但是大家也可以用MVC或MVVM等模式。这里我不再赘述细节,但是需要强调的是,这里的fragment和
activity都是View,其内部除了UI逻辑以外没有其他逻辑,这也是所有渲染的东西发生的地方。
本层次的Presenter由多个interactor(用例)组成,Presenter在 android UI 线程以外的新线程里工作,并通过回调将要渲染到View上的数据传递回来。

如果你需要一个使用MVP和MVVM的Effective Android UI典型案例,可以参考我朋友Pedro Gómez的文章。
领域层 (Domain Layer)
这里的业务规则是指所有在本层发生的逻辑。对于Android项目来说,大家还可以看到所有的interactor(用例)实施。这一层是纯粹的java模块,没有任何的Android依赖性。当涉及到业务对象时,所有的外部组件都使用接口。

数据层 (Data Layer)
应用所需的所有数据都来自这一层中的UserRepository实现(接口在领域层)。这一实现采用了Repository Pattern,主要策略是通过一个工厂根据一定的条件选取不同的数据来源。
比如,通过ID获取一个用户时,如果这个用户在缓存中已经存在,则硬盘缓存数据源会被选中,否则会通过向云端发起请求获取数据,然后存储到硬盘缓存。
这一切背后的原理是由于原始数据对于客户端是透明的,客户端并不关心数据是来源于内存、硬盘还是云端,它需要关心的是数据可以正确地获取到。

注:在代码方面,出于学习目的,我通过文件系统和Android preference实现了一个简单、原始的硬盘缓存。请记住,如果已经存在了能够完成这些工作的库,就不要重复制造轮子。
错误处理
这是一个长期待解决的讨论话题,如果大家能够分享各自的解决方案,那真真是极好的。
我
的策略是使用回调,这样的话,如果数据仓库发生了变化,回调有两个方法:onResponse()和onError().
onError方法将异常信息封装到一个ErrorBundle对象中:
这种方法的难点在于这其中会存在一环扣一环的回调链,错误会沿着这条回调链到达展示层。因此会牺牲一点代码的可读性。另外,如果出现错误,我本来可以通过
事件总线系统抛出事件,但是这种实现方式类似于使用C语言的goto语法。在我看来,当你订阅多个事件时,如果不能很好的控制,你可能会被弄得晕头转向。
测试
关于测试方面,我根据不同的层来选择不同的方法:
- 展示层 ( Presentation Layer) : 使用android instrumentation和 espresso进行集成和功能测试
- 领域层 ( Domain Layer) : 使用JUnit和Mockito进行单元测试;
- 数据层 ( Data Layer) : 使用Robolectric ( 因为依赖于Android SDK中的类 )进行集成测试和单元测试。
代码展示
我猜你现在在想,扯了那么久的淡,代码究竟在哪里呢? 好吧,这就是你可以找到上述解决方案的github链接。还要提一点,在文件夹结构方面,不同的层是通过以下不同的模块反应的:
- presentation: 展示层的Android模块
- domain: 一个没有android依赖的java模块
- data: 一个数据获取来源的android模块。
- data-test: 数据层测试,由于使用Robolectric 存在一些限制,所以我得再独立的java模块中使用。
结论
正
如 Bob大叔 所说:“Architecture is About Intent, not Frameworks”
,我非常同意这个说法,当然了,有很多不同的方法做不同的事情(不同的实现方法),我很确定,你每天(像我一样)会面临很多挑战,但是遵循这些方法,可以
确保你的应用会:
- 易维护
- 易测试
- 高内聚
- 低耦合
最后,我强烈推荐你去实践一下,并且分享你的经验。也许你会找到更好的解决方案:我们都知道,不断提升自己是一件件非常好的事。我希望这篇文章对你有所帮助,欢迎拍砖。
一种更清晰的Android架构(转)的更多相关文章
- android学习——android架构
android架构:在了解全局的情况下进行细致化的分析才能更有效的学习android的运行原理,才能更深刻的理解android开发: 1.架构图直观 2.架构详解 2.1.Linux Kernel 2 ...
- Android架构初探
#一 背景点评美团合并之后,业务需要整合,我们部门的几条业务需要往美团平台迁移,为了降低迁移成本,开发和维护成本,以及将来可能要做的单元测试,需要对架构进行相应的调整.之前的代码都堆在Activity ...
- Android架构组件——ViewModel
概述 ViewModel,从字面上理解的话,它肯定是跟视图(View)以及数据(Model)相关的.正像它字面意思一样,它是负责准备和管理和UI组件(Fragment/Activity)相关的数据类, ...
- 一种M2M业务的架构及实现M2M业务的方法
http://www.cnblogs.com/coryxie/p/3849764.html 技术领域 [0001] 本发明涉及通信技术领域,尤其涉及一种M2M业务的架构及实现M2M业务的方法. 背景技 ...
- 从Microsoft.AspNet.Identity看微软推荐的一种MVC的分层架构
Microsoft.AspNet.Identity简介 Microsoft.AspNet.Identity是微软在MVC 5.0中新引入的一种membership框架,和之前ASP.NET传统的mem ...
- Android架构分析之Android消息处理机制(二)
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本号:4.4.2 在上一篇文章中我们看了一个使用Handler处理Message消息的样例,本文我们 ...
- Android架构分析之使用自定义硬件抽象层(HAL)模块
作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz Android版本:2.3.7_r1 Linux内核版本:android-goldfish-2.6.29 在上一篇博 ...
- Android架构设计和软硬整合完整训练
Android架构设计和软硬整合完整训练 Android架构设计和软硬整合完整训练:HAL&Framework&Native Service&Android Service&a ...
- [Android 泥水匠] Android基础 之一:浅谈Android架构到HelloWorld案例的剖析
作者:泥沙砖瓦浆木匠网站:http://blog.csdn.net/jeffli1993个人签名:打算起手不凡写出鸿篇巨作的人,往往坚持不了完成第一章节. 交流QQ群:[编程之美 365234583] ...
随机推荐
- No Spring WebApplicationInitializer types detected on classpath。启动时不报错,但是页面打不开。
一片红,没有黑色disPatcher的加载. 百度,但是没有用,二十分钟浪费,这个问题的本质就是web.xml中的disPatcher没有加载,但是我肯定和代码无关,配置文件也没有变化过,值可能是to ...
- Oracle分页查询
1.无ORDER BY排序的写法.(效率最高) 经过测试,此方法成本最低,只嵌套一层,速度最快!即使查询的数据量再大,也几乎不受影响,速度依然! sql语句如下: SELECT * FROM (Sel ...
- FusionCharts饼图的图例属性
showLegend 是否在图表中显示图例 legendPosition 图例可以显示在图表的底部(BOTTOM)或右侧(RIGHT) legendCaption 可以为图例整体定义一个标题 lege ...
- python 学习笔记
>>> help(print)Help on built-in function print in module builtins: print(...) print(value, ...
- unity3D5旧动画系统注意事项
最近在写人物控制时因为习惯用旧动画系统所以也没想那么多,就直接在新系统下按照老样子写了,突然发现animation.play不能用了,后来重新声明了变量: public Animation anim; ...
- Qt之QStringList讲解
QStringList类提供了一个字符串列表 从QString继承而来,它提供快速索引为基础的接入以及快速插入和清除. 成员函数用于操作这个字符串列表如: append(),insert(),repl ...
- font-family:“微软雅黑” OR font-family:Microsoft Yahei
sublime对中文编码支持的不好,可以考虑用后者.
- ClientScript.RegisterStartupScript 不起作用
asp.net webform 使用 ClientScript.RegisterStartupScript 不起作用 form 加上 runat="server",ok
- wex5 实战 手指触屏插件 hammer的集成与优劣
前言 前几天,给客户做了一个图片点击放大,很简单,客户说能不能双手指缩放图片呢? 想到了hammer,不管好用不好用,总得试. 网上居然没有像样的中文文档和成熟案例,有的文写的鬼都看不懂.还是自已动手 ...
- requirejs基础教程
一.初识requirejs 随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿,原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,我们需要团队协作.模块复用. ...