安卓架构...有什么清晰的方式?
前言
我们知道写出有质量的软件是复杂而且困难的:它不仅仅在于满足所有的需求,同时也应该是健壮的、易于维护的、方便测试的、非常灵活的(能够灵活的改变内容,如模块加减)。清晰的架构(The Clean Architecture)就是在这种需求下诞生,而且能够成为在软件开发过程中的一个好的选择。
清爽的架构的想法非常简单:它代表一组方式规则,能够产生如下的系统:
- 与框架无关
- 易于测试
- 与UI无关
- 与数据库无关
- 与其他外部组件无关
在实际应用过程中,没有必要像图中那样来进行划分,因为上图只是概要图。但是应该做到单向依赖:代码的依赖性只能够向内, 所有内部的模块不应该知道任何外部模块的信息,更不要说有任何调用。
下面是一些相关词汇,用来帮助理解:
- 实例(Entities): 这些是软件实现过程中的业务实例(Object)。
- 数据交互器(Use Cases,又称interactor):
数据交互器负责协调实例的数据传输。 - 接口适配器(Interface Adapters): 接口适配器负责数据转换,将从实例和数据交互器中传递出来的数据进行解析。Presenter和Controller就属于这里。 (从原文中可以理解,在设计usecase 和entities的时候,尽量让这两层的数据变得简单、易于使用,提升这两层的效率。这样,将更深层中的工作量变得更少。只是个人理解。)
- 框架和驱动(Frameworks and Drivers): 这一部分负责所有相关细节,如: UI, 工具,框架等(这里暂时无法理解)。
需要更加清晰的理解,可以翻墙看下链接中的视频。
安卓架构
架构的目的是分离原则:内部的业务逻辑完全与外部无关,因此,在测试中就不需要加载外部依赖。
为了实现这个目标,我建议将程序分成三层,其中每一层有自己的工作,并且在运行过程中与其它层隔离开来。
值得一提的是,通过为每一层建立自己的数据模型,能够实现各层独立。(在代码中你会发现,需要用data mapper来实现数据转换,作为代价,你的model将横跨整个APP。)
上面内容的图解:
注: 我没有用任何外部库(除了Gson来解析json数据,和junit,mockito,robolectric,espresso用来测试)。这样,我就能把例子弄得更加清楚。但是,千万不要自己重新去写一个轮子,能从别人那儿拿来用的就从别人那拿(你要好好挑一挑)。
展示层(Presentation Layer)
与页面逻辑和页面动画相关的逻辑在这一层中。它主要用的是MVP架构。你也可以按照需要使用MVC,MVVM等。值得强调的是,在展示层,fragment和activity仅仅是View,其中没有任何除了UI逻辑以外的逻辑,同时渲染也在这一层被实现。
Presenter在这一层中与数据交互器(interactor,use case)一起以新线程(UI线程外)的方式工作,然后通过回调函数将需要展示的数据交给View。
逻辑层(Domain layer)
本层业务规则为: 所有的逻辑在这一层发生。在项目层面,你会发现所有的数据交互器在这一层被实现。
这一层为纯java模块,不包括任何对android的依赖。所有的层外组件通过接口与逻辑层中的实例(Object)交互。
数据层(Data Layer)
数据层就像一个仓库一样,所有APP需要的数据都通过位于逻辑层中的仓库接口(Repository interface)被取出。这个接口使用了仓库模式,也就是,通过一个工厂,按需挑选出各种数据向上传递。打个比方,当通过id获取用户的时候,程序会判断缓存中是否有用户数据,如果没有的话,从服务器中获取,并存在本地,如果有的话,直接从缓存中获取。
用这种方式的原因是:用户不在乎数据的来源是什么,他们只在乎数据是否被获取到。
注意:代码中有这一接口的简单例子。还是那句话,不要重新做轮子。
错误处理
我的策略是使用回调函数,因此,比如在数据仓库中有情况发生,将产生两个回调函数,onResponse()和onError()。后者将把异常打包进入一个叫做“ErrorBundle”的包装类: 这个方式带来了一定的问题,因为这样产生了一连串的,一层层上报的回调函数,直通展示层,被展示层处理掉。这种方式在一定程度上降低了代码可读性。
另一方法是,我可以实现EventBus,每次有错误发生,EventBus将会将时间分配给指定处理函数。但是这样需要非常严谨,小心的处理,否则会不好管理。
测试
关于测试,我给出一点建议。
- 展示层:使用安卓自动化测试工具instrumentation和espresso来实现集成和功能性测试。
- 逻辑层:使用Junit 和 mockito来进行单元测试。
- 数据层:Robolectric(因为本层有安卓依赖)和junit以及mockito来进行测试。
结论
架构的设计是以实际情况为走向,而不是框架。要根据实际情况来进行架构设计。在设计架构的时候要确保:
- 易于维护
- 方便测试
- 高内聚
- 低耦合
代码例子: 参见github