Nathing
分类: Android平台
2015-12-18 15:30:16
Android的开发生态系统发展迅速,每周都有变化,人们不停地创建新工具、更新资源库、撰写博文、发表演讲。只要享受一个月的假期,回来的时候支持库和/或Play Services都更新换代了。
笔者与合作开发Android应用已有超过三年时间。在这段时间里,我们用来构建Android应用的架构与技术一直在不断进化。在本文中,我们将具体阐述这些架构变更背后的经验、失误还有推论。
早在2012年,我们的代码库总是采用基础架构,并未使用任何网络库,还是用老一套的AsyncTasks。下面的图表粗略地演示了这个架构。
代码共分两层:控制从REST API检索/保存数据的数据层(data layer),还有负责在UI上控制与展示数据的视图层(view layer)。
APIProvider提供方法,让Activities和Fragments能够很容易地与REST API交互。运用URLConnection和AsyncTasks来执行单独线程中的网络调用,并通过回调向Activities返回结果。
类似地,CacheProvider包含了从SharedPreferences或SQLite数据库检索存储数据的方式,通过回调将结果返回给Activities。
这个办法的主要问题在于,视图层责任过大。试想一个简单的通用场景:应用程序在加载文章列表时,将其缓存到SQLite数据库中,并最终展示在ListView中。具体执行如下:
这是个简单的例子。在真实案例场景中,REST API可能不会按照浏览所需的那样返回数据,因此Activity会设法在展示数据之前对其进行转换或过滤。另一个常见案例:在使用loadPosts() 方法获取需要从别处拿到的参数时,比如由Play Services SDK提供的电子邮件地址,很有可能SDK会通过回调异步返回邮件,也就是说我们现在有三层嵌套回调(nested callbacks)。如果复杂性继续增加,这个方法会导致所谓的回调地狱(callback hell)。
总结:
差不多在两年时间中,我们都在采用前面描述的那种架构。在那段时间里,我们做了一些修正,但是解决问题时收效甚微。例如,我们增加了一些helper类,以减少Activities和Fragments中的代码,并开始在APIProvider中使用Volley。尽管如此,在应用代码测试时还是面临测试友好性问题与回调地狱频繁出现的问题。
直到2014年我们发现了,在尝试了几个样例项目后,我们发现这可能是解决嵌套回调问题的终极解决办法。如果对响应式编程不熟悉的话,可以参考。简单来讲,RxJava允许用户通过异步流管理数据,并提供很多可用在事件流中的operator,方便用户修改、筛选或合并数据。
考虑到前些年遭受的痛苦,我们开始考虑新应用的架构是什么样的,然后得出了这个。
与头一个方法类似,这个架构也可以分为两层,分别是数据层与视图层。数据层包含DataManager,还有一系列helper。视图层由诸如Fragments、Activities、ViewGroups等Android框架组件构成。
Helper类(图表第三列)包含具体的职责,同时执行方式也很简洁。例如大多项目包含访问REST API的helper,从数据库读取数据的helper或者与第三方SDK交互的helper。不同的应用程序包含不同数量的helper,不过最常见的helper有:
大多数helper类中的公共方法会返回RxJava Observables。
DataManager是这个架构的核心,它广泛运用了RxJava operator来合并、筛选与转换从helper类中获得的数据。DataManager的目标是通过提供准备显示的数据,来减少Activities和Fragments的工作量,而且这些数据一般无需任何转换。
下面的代码就是DataManager方法的实例。
点击(此处)折叠或打开
像Activities或Fragments之类的视图层组件会简单调用这个方法,并订阅返回的Observable。一旦订阅完成,Observable所发出的不同文章就能直接加入到Adapter中,以便在RecyclerView或类似组件中显示。
这个架构的最后一个元素是Eventbus(事件总线),它允许我们将数据层的事件进行广播,因此视图层的多个组件能够订阅这些事件。例如,DataManager中的signOut()方法可以在Observable完成时发布一个事件,让多个订阅这个事件的Activities修改UI,显示为登出状态。
为什么这个方法更好?
还有什么问题呢?
在过去的一年中,像MVP、MVVM这样的一些架构模型在Android社区受到了热捧。在与中研究过这些模型之后,我们发现MVP能够对我们目前的方法带来很有价值的改进。由于我们目前的架构分为两层(视图与数据层),加上MVP也很自然。我们只需增加一个新的展示层(a new layer of presenters),将一部分代码从视图层移过去就可以了。
数据层保持不变,不过现在改名为模型层(Model),以便名符其实。
展示层控制加载来自模型层的数据,并在结果准备好之后调用视图层的正确方法来显示。它订阅DataManager返回的Observables,因此必须处理类似调度与之类的工作。此外,它可以分析错误代码,或者在需要时在数据流中应用额外操作。例如,如果我们需要筛选一些数据,而这个筛选无法在其他地方复用,那么用展示层来实现会比在DataManager实现要更好。
下面是在展示层中公共方法的案例。这部分代码订阅了从dataManager.loadTodayPosts()方法返回的Observable。
点击(此处)折叠或打开
mMvpView是这个展示层正在assist的视图层组件。一般MVP视图是Activity、Fragment或ViewGroup实例。
就像之前的架构那样,视图层包含像ViewGroups、Fragments或Activities这样的标准框架组件。这些组件的主要区别在于没有直接订阅Observables,而是执行MVP视图,提供一系列类似showError() 或showProgressIndicator()之类的简明方法。视图组件还控制处理类似点击事件之类的与用户交互,并通过调用展示层的正确方法来执行。例如,如果我们有一个加载文章列表的按钮,Activity就会从onClick监听那里调用presenter.loadTodayPosts()。
为什么这个方法更好?
还有什么问题?
需要注意的是,这个架构并不完美。事实上,认为它是唯一而且完美的架构,能够一劳永逸的解决问题这样的想法太过天真。Android的生态系统会继续保持高速发展,我们必须持续探索、阅读、实验,才能找到构建优秀Android应用的更佳途径。
(翻译/孙薇 审校/唐小引)
集成模型视图显示
想要查看基于MVP的完整架构,请查看或者。