译安卓应用架构体系
#安卓应用架构体系
我们经历了从标准的Activity加AsyncTask架构到当前流行的用RxJava驱动的MVP模式为基础的架构体系。
不同部分的软件模块应该是独立的,但放在一起工作时却想是个烂醉的机器–Chester Alvarez拍的照片
安卓开发生态步伐很快,每个星期都有新的工具被创建,新的库被更新,新的博客文章发出来,新的话题被讨论。如果你去度假了一个月,你回来的时候就会有新版本的支持库或者新的服务等你去处理。 我在ribot团队做了3年的安卓应用。在此期间,我们使用的安卓应用技术和架构已经连续演变了很多次。这篇文章将带你经历讲述我们是如何在架构变化下学习,出错并且找到原因的。
===
###旧时代
回到2012年我们底层习惯使用基础的结构。我们不使用任何的网络库但AsyncTask任然是我们的朋友。下面的图差不多展示这个架构是个怎样子的。
初始的架构
程序使用两层结构:数据层负责使用REST API检索和保存数据并持久化数据存储。视图层负责调用句柄和在UI上显示数据。 APIProvider 提供方法使得Activity和Fragment容易用REST API相互通信。这些方法使用URLConnection和AsyncTask执行网络调用在分开的线程里并经由回调返回结果给Activity。 同样的,CacheProvider包含了些方法用SharedPreference或者SQLite数据库,检索和存储数据。它也通过回调把结果返回给Activity。
###问题集
这个途径最主要的问题是视图层负责的东西太多。想象一个的常用的方案,一个应用必须去加载一串博客文章,用SQLite数据库缓存他们并最终在ListView上展示他们。Activity会做以下的事情。
1.在APIProvider里调用loadPosts方法(回调)。
2.等待APIProvider回调成功然后在CacheProvider里调用savePosts(回调)。
3.等待CacheProvider回调成功然后在ListView上展示文章。
4.分别处理从APIProvider和CacheProvider这两个可能的错误回调。
这是个非常简单的例子。在实际的案例方案里REST API可能不会返回视图需要的数据。因为Activity将需要以某种方法在展示前转换或过滤数据。另一个常用的案例是当loadPosts()方法带上一个需要从其他地方获取的参数时,比如SDK服务提供的一个邮件地址。SDK貌似会以异步回调方式返回邮件地址,这意味着我们现在有3层嵌套回调。如果我们继续增加复杂度,众所周知这个途径的结果是将会变成回调的地狱。
###小结
1.Activity和Fragment会变得很大并且难以维护。
2.太多的嵌套回调意味着程序是丑陋的并且难以理解,所以在更换和增加新的功能时会痛苦。
3.单元测试变得有挑战性或者不太可能了,因为太多逻辑放在Activity和Fragment里,这样会导致单元测试会很费劲。
一个由RxJave驱动的新架构
我们用前面的方法大概2年时间。在此期间,我们做了一些改进,稍微减轻前面描述的问题。 一直到2014年我们开始看RxJava。在尝试一些试验工程后,我们认识到它能最终解决嵌套回调的问题。如果你不熟悉反应式系统编你可以看这个介绍(https://gist.github.com/staltz/868e7e9bc2a7b8c1f754) 。简而言之,RxJava能让你通过异步流管理数据并且给你很多操作,你可以应用到数据流里去转换,过滤或者合并数据。 考虑到前些年痛苦的经验,我们开始考虑在新的app里采用怎样的架构。然后我们开始这样的做了。
###RxJava驱动的架构体系
与第一个方法相似,这个架构也能被拆分成数据和视图层。数据层包括DataManager和一些工具。视图层由Android框架组件的形式例如Fragment,Activity,ViewGroup等。 工具类(图形的第三列)是非常特殊的责任,用简洁方式实现了他们。例如,大多数项目有访问REST API的工具,读取数据库数据或者与第三方SDK通信。不同的应用有不同数量的工具但大多数工具为如下:
1.PreferencesHelper:用SharePreferences读取和保存数据
2.DatabaseHelper:处理存取SQLite数据库
3.Retrofit(https://github.com/square/retrofit) 服务:执行调用REST API。我们已经开始使用Retrofit代替Volley因为它提供对RxJava的支持。它也非常好用。
工具类里面的大多数公有方法会返回RxJava的Observable。 DataManager是架构的大脑。它广泛使用RxJava操作去合并过滤转换数据并用工具类检索。DataManager的目的是去大量减少Activity和Fragment处理准备展示数据的工作,不再常需要任何转换。
下面条目展示的是DataManager方法是怎样的。样例方法是如下工作的: 1.调用Retrofit服务从REST API加载一串博客文章。
2.使用DatabaseHelper保存文章到本地数据库以达到缓存的目的。
3.过滤今天写的博客文章因为这是视图层想要展示的东西。
public Observable<Post> loadTodayPosts() {
return mRetrofitService.loadPosts()
.concatMap(new Func1<List<Post>, Observable<Post>>() {
@Override
public Observable<Post> call(List<Post> apiPosts) {
return mDatabaseHelper.savePosts(apiPosts);
}
})
.filter(new Func1<Post, Boolean>() {
@Override
public Boolean call(Post post) {
return isToday(post.date);
}
});
}
视图中的组件像Activity或Fragment会简单调用这个方法并订阅返回的Observable。一旦订阅结束,用Observable得到的不同的文章能直接加入到适配器去展示在RecyclerView或者类似地方。
文章最后一个元素是事件总线。事件总线允许我们广播那些发生在数据层上的事件,所以在视图层里的多种组件可以订阅这些事件。例如,在DataManager里的asignOut()方法可以推出一个事件当Observable完成时,然后在这个事件里被订阅的多种Activity就可以更换他们的UI来展示一个不在线的信号。
###为什么这个方法更好
1.RxJava 的Observable和操作者去除了嵌套回调的需求。
2.DataManager接管了以前视图部分的工作。因此它使得Activity和Fragment更加轻便。
3.把Activity和Fragment的代码迁移到DataManager和工具类意味着单元测试变得更加容易。
4.整理拆分责任并让DataManager作为与数据层通信的唯一途径,这样使得这个架构测试友好。工具类和DataManager可以被容易模仿。
###我们仍然存在什么问题?
1.对于大型和复杂的项目来说DataManager会变得太臃肿并且难以维护。
2.虽然视图层组件像Activity和Fragment变得更加轻便了,但他们仍然需要去处理相当部分围绕管理RxJava的订阅逻辑和错误分析等。
###整合MVP(模型-视图-发言者)
在过去几年,一些架构模式像MVP或者MVVM在安卓社区曾获得流行。在一个样例工程和文章上探索这些模式后,我们发现MVP能带来很好的价值来改进我们现存的方法。因为我们现在架构被分成2个层级(视图和数据),加上MVP后显得自然。我们仅仅需要加入一个新的发言者层级并把视图层的代码迁移到发言者层上就可以。
MVP为基础的架构
数据层保留下来但现在叫模型层了为了与模式的名字更加匹配。
发言者是负责从模型层加载数据然后调用视图层的指定方法当结果准备好的时候。他们用DataManger订阅了Observable的返回。因为他们要去处理一些事情像调度程序和订阅一样。另外,他们可能分析错误代码或者应用特殊的操作在数据流上如果需要的话。例如,如果我们需要过滤一些数据并且这个过滤器不想被在其他地方重复使用,这样在发言者里实现比在DataManager里实现更有意义。
下面你可以看到一个公有的方法在发言者里是怎样的。这个代码用thedataManager.loadTodayPosts() 方法订阅了Observable返回,它被定义在前面提到的部分。
MvpView是视图组件而发言者是协助的。通常MVP视图是Activity,Fragment或者ViewGroup的实例。 如以前的架构,视图层包含标准框架组件例如ViewGroup,Fragment或Activity。主要的区别是这些组件不直接订阅Observable。他们用实现MvpView接口替代并提供一串简洁的方法例如showError()或者showProgressIndicator()。这视图的组件同样负责处理用户交互例如点击事件和行为通过在发言者里调用指定的方法。例如,我们有一个按钮加载一串文章,我们的Activity会从onClick监听者中调用presenter.loadTodayPosts()。
如果你想了解一整个MVP为基础架构的工作样例,你可以渐出我们的Android Boilerplatex(https://github.com/ribot/android-boilerplate)项目在Github上,你也可以在ribot的指南(https://github.com/ribot/android-guidelines/blob/master/architecture_guidelines/android_architecture.md)上了解更多。
###为什么这个方法更好?
1.Activity和Fragment变得很轻便。他们的责任只有装配和更新UI并且处理用户事件。因此他们变得很容易去维护。
2.现在我们能容易的通过模仿视图层为发言者写单元测试。之前,这个程序是视图层的一部分所以我们不能对它单元测试。整个架构变得非常测试友好。
3.如果DataManager开始变得臃肿,我们能通过迁移代码到发言者上来减缓这个问题。
###我们任然存在的问题是什么?
当有一个单例DataManager仍然有一个问题当底层变得很大很复杂的时候。我们还没有达到这个真正问题点,但我们意识到他可能会发生。
重点提下这个不是完美的架构。实际上,认为世界上有一个唯一并且完美的架构能永远解决你所有的问题是很幼稚天真的。安卓社区会持续快速发展,我们需要保持探索,阅读和尝试,这样我们可以找到一个更好的方式去继续构建优秀的安卓应用。 希望你喜欢这篇文章并且能对你有用。如果这真是这样的话,不要忘了点击推荐按钮。也欢迎能听到你自己对这些方法的想法。
翻译自:https://medium.com/ribot-labs/android-application-architecture-8b6e34acda65#.1qvkq2xaa
原文作者: Ivan
作者授权邮件:
申明此篇文章为本人参加GAD翻译比赛文章
转载请注明出处: http://www.luzexi.com
感谢您的耐心阅读
Thanks for your reading
版权申明
本文为博主原创文章,未经允许不得转载:
Copyright attention
Please don't reprint without authorize.
微信公众号,文章同步推送,致力于分享一个资深程序员在北上广深拼搏中对世界的理解
QQ交流群: 777859752 (高级程序书友会)