转载地址:http://www.codeceo.com/article/android-artch-change.html
软件代码库各个不同的部分应当彼此独立,其整体却犹如一部运转良好的机器
Android的开发生态系统发展迅速,每周都有变化,人们不停地创建新工具、更新资源库、撰写博文、发表演讲。只要享受一个月的假期,回来的时候支持库和/或Play Services都更新换代了。
Android应用架构变更背后的经验、失误与推论
笔者与 ribot团队 合作开发Android应用已有超过三年时间。在这段时间里,我们用来构建Android应用的架构与技术一直在不断进化。在本文中,我们将具体阐述这些架构变更背后的经验、失误还有推论。
过去
早在2012年,我们的代码库总是采用基础架构,并未使用任何网络库,还是用老一套的AsyncTasks。下面的图表粗略地演示了这个架构。
Android应用架构变更背后的经验、失误与推论
初始架构
代码共分两层:控制从REST API检索/保存数据的数据层(data layer),还有负责在UI上控制与展示数据的视图层(view layer)。
APIProvider提供方法,让Activities和Fragments能够很容易地与REST API交互。运用URLConnection和AsyncTasks来执行单独线程中的网络调用,并通过回调向Activities返回结果。
类似地,CacheProvider包含了从SharedPreferences或SQLite数据库检索存储数据的方式,通过回调将结果返回给Activities。
问题
这个办法的主要问题在于,视图层责任过大。试想一个简单的通用场景:应用程序在加载文章列表时,将其缓存到SQLite数据库中,并最终展示在ListView中。具体执行如下:
调用APIProvider中的loadPosts(回调)方法;
等待APIProvider成功回调,然后调用CacheProvider中的savePosts(回调);
等待CacheProvider成功回调,然后在ListView中显示文章;
分别处理APIProvider和CacheProvider的回调错误。
这是个简单的例子。在真实案例场景中,REST API可能不会按照浏览所需的那样返回数据,因此Activity会设法在展示数据之前对其进行转换或过滤。另一个常见案例:在使用 loadPosts() 方法获取需要从别处拿到的参数时,比如由Play Services SDK提供的电子邮件地址,很有可能SDK会通过回调异步返回邮件,也就是说我们现在有三层嵌套回调(nested callbacks)。如果复杂性继续增加,这个方法会导致所谓的回调地狱(callback hell)。
总结:
Activities和Fragments逐渐过大而难以维护;
嵌套回调太多,导致代码丑陋不堪,难以理解与修改,也不好增加新功能;
单元测试也颇有难度,即便勉强进行,由于Activities或Fragments中包含有大量逻辑,相关工作也会相当费劲。
由RxJava驱动的新架构
差不多在两年时间中,我们都在采用前面描述的那种架构。在那段时间里,我们做了一些修正,但是解决问题时收效甚微。例如,我们增加了一些helper类,以减少Activities和Fragments中的代码,并开始在APIProvider中使用 Volley 。尽管如此,在应用代码测试时还是面临测试友好性问题与回调地狱频繁出现的问题。
直到2014年我们发现了 RxJava ,在尝试了几个样例项目后,我们发现这可能是解决嵌套回调问题的终极解决办法。如果对响应式编程不熟悉的话,可以参考 这篇简介 。简单来讲,RxJava允许用户通过异步流管理数据,并提供很多可用在事件流中的 operator ,方便用户修改、筛选或合并数据。
考虑到前些年遭受的痛苦,我们开始考虑新应用的架构是什么样的,然后得出了这个。
Android应用架构变更背后的经验、失误与推论
与头一个方法类似,这个架构也可以分为两层,分别是数据层与视图层。数据层包含DataManager,还有一系列helper。视图层由诸如Fragments、Activities、ViewGroups等Android框架组件构成。
Helper类(图表第三列)包含具体的职责,同时执行方式也很简洁。例如大多项目包含访问REST API的helper,从数据库读取数据的helper或者与第三方SDK交互的helper。不同的应用程序包含不同数量的helper,不过最常见的helper有:
PreferencesHelper:在SharedPreferences中读取与保存数据。
DatabaseHelper:处理SQLite数据库的访问。
Retrofit 服务:从REST API执行调用。我们使用Retrofit来代替Volley,因为它提供了对RxJava的支持,也更好用。
大多数helper类中的公共方法会返回RxJava Observables。
DataManager是这个架构的核心,它广泛运用了RxJava operator来合并、筛选与转换从helper类中获得的数据。DataManager的目标是通过提供准备显示的数据,来减少Activities 和Fragments的工作量,而且这些数据一般无需任何转换。
下面的代码就是DataManager方法的实例。
调用Retrofit服务来加载从REST API获取的文章列表。
用DatabaseHelper在本地数据库中保存文章,做缓存使用。
按照视图层的需求,筛选出今天撰写的文章。
public Observable loadTodayPosts() {
return mRetrofitService.loadPosts()
.concatMap(new Func1