Android开发生态圈的节奏非常之快。每周都会有新的工具诞生,类库的更新,博客的发表以及技术探讨。如果你外出度假一个月,当你回来的时候可能已经发布了新版本的Support Library或者Play Services
我与Ribot Team一起做Android应用已经超过三年了。这段时间,我们所构建的Android应用架构和技术也在不断地演变。本文将向您阐述我们的经验,错误以及架构变化背后的原因。
曾经的架构
追溯到2012年我们的代码库使用的是基本结构,那个时候我们没有使用任何第三方网络类库,而且AsyncTask
也是我们的好朋友。当时的架构可以大致表示为下图。
代码被划分为两层结构:Data Layer(数据层)负责从REST API或者持久数据存储区检索和存储数据;View Layer(视图层)的职责是处理并将数据展示在UI上。
APIProvider提供了一些方法,使Activity和Fragment能够很容易的实现与REST API的数据交互。这些方法使用URLConnection和AsyncTask在一个单独的线程内执行网络请求,然后通过回调将结果返回给Activity。
按照同样的方式,CacheProvider 所包含的方法负责从SharedPreferences和SQLite数据库检索和存储数据。同样使用回调的方式,将结果传回Activity。
存在的问题:
使用这种结构,最主要的问题在于View Layer持有太多的职责。想象一个简单且常见的场景,应用需要加载一个博客文章列表,然后缓存这些条目到SQLite数据库,最后将他们展示到ListView等列表视图上。Activity要做到以下几个步骤:
通过APIProvider调用loadPosts
方法(回调)
等待APIProvider的回调结果,然后调用CacheProvider中的savePosts
方法(回调)
等待CacheProvider的回调结果,然后将这些文章展示到ListView等列表视图上
分别处理APIProvider和CacheProvider回调中潜在的异常。
这是一个非常简单的例子,在实际开发环境中REST API返回的数据可能并不是View直接需要的。因此,Activity在进行展示之前不得不通过某种方式将数据进行转换或过滤。另一个常见的情况是,调用loadPosts( )
所需要的参数,需要事先从其他地方获取到,比如,需要Play Services SDK提供一个Email地址参数。就像SDK通过异步回调的方式返回Email地址,这就意味着现在我们至少有三层嵌套的回调。如果继续添加复杂的业务逻辑,这种架构就会陷入众所周知的Callback Hell(回调地狱)。
总结:
Activitty和Fragment变得非常庞大并且难以维护。
太多的回调嵌套意味着丑陋的代码结构而且不易读懂和理解。如果在这个基础上做更改或者添加新特性会感到很痛苦。
单元测试变得非常有挑战性,如果有可能的话,因为很多逻辑都留在了Activity或者Fragment中,这样进行单元测试是很艰难的。
RxJava驱动的新型架构
我们使用上文提到的组织架构差不多两年的时间。在那段时间内,我们做了一些改进,稍微缓解了上述问题。例如,我们添加了一些Helper Class(帮助类)用来减少Activity和Fragment中的代码,在APIProvider中使用了Volley。尽管做出了这些改变,我们应用程序的代码还是不能进行友好的测试,并且Callback Hell(回调地狱)的问题还是经常发生。
直到2014年我们开始了解RxJava。在尝试了几个示例项目之后,我们意识到她可能最终帮助我们解决掉嵌套回调的问题。如果你还不熟悉响应式编程,可以阅读本文(译者注:译文点这里那些年我们错过的响应式编程)。简而言之,RxJava允许通过异步流的方式处理数据,并且提供了很多操作符,你可以将这些操作符作用于流上从而实现转换,过滤或者合并数据等操作。
考虑到经历了前几年的痛苦,我们开始考虑,一个新的应用程序体系架构看起来会是怎样的。因此,我们想出了这个。
类似于第一种架构,这种体系架构同样被划分为Data Layer和View Layer。Data Layer持有DataManager
和一系列的Helper classe 。View Layer由Android的Framework组件组成,例如,Fragment,Activity,ViewGroup等。
Helper classes(图标中的第三列)有着非常特殊的职责以及简洁的实现方式。例如,很多项目需要一些帮助类对REST API进行访问,从数据库读取数据,或者与三方SDK进行交互等。不同的应用拥有不同数量的帮助类,但也存在着一些共性:
PreferencesHelper:从SharedPreferences
读取和存储数据。
DatabaseHelper:处理操作SQLite数据库。
Retrofit services:执行访问REST API,我们现在使用Retrofit来代替Volley,因为它天生支持RxJava。而且也更好用。
帮助类里面的大多数public方法都会返回RxJava的Observable
。
DataManager是整个架构中的大脑。它广泛的使用了RxJava的操作符用来合并,过滤和转换从帮助类中返回的数据。DataManager旨在减少Activity和Fragment的工作量,它们(译者注:指Activity和Fragment)要做的就是展示已经准备好的数据而不需要再进行转换了。
下面这段代码展示了一个DataManager方法可能的样子。这个简单的示例方法如下:
调用Retrofit service从REST API加载一个博客文章列表
使用DatabaseHelper保存文章到本地数据库,达到缓存的目的
筛选出今天发表的博客,因为那才是View Layer想要展示的。
public Observable
在View Layer中诸如Activity或者Fragment等组件只需调用这个方法,然后订阅返回的Observable
即可。一旦订阅完成,通过Observable发送的不同博客,就能够立即被添加进Adapter从而展示到RecyclerView或其他类似控件上。
这个架构的最后元素就是Event Bus(事件总线)。它允许我们在Data Layer中发送事件,以便View Layer中的多个组件都能够订阅到这些事件。比如DataManager中的退出登录方法可以发送一个事件,订阅这个事件的多个Activity在接收到该事件后就能够更改它们的UI视图,从而显示一个登出状态。
为什么这种架构更好?
DataManager接管了以前View Layer的部分职责。因此,它使Activity和Fragment变得更轻量了。
将代码从Activity和Fragment转移到了DataManager和帮助类中,就意味着使写单元测试变得更简单。
明确的职责分离和DataManager作为唯一与Data Layer进行交互的点,使这个架构变得Test-Friendly。帮助类和DataManager能够很容易的被模拟出来。
我们还存在什么问题?
对于庞大和复杂的项目来讲,DataManager会变得非常的臃肿和难以维护。
尽管View Layer诸如Activity和Fragment等组件变得更轻量,它们让然要处理大量的逻辑,如管理RxJava的订阅,解析错误等方面。
集成MVP
在过去的一年中,几个架构设计模式,如MVP或者MVVM在Android社区内已经越来越受欢迎了。通过在[示例工程]( sample project)和文章中进行探索后,我们发现MVP,可能给我们现有的架构带来非常价值的改进。因为当前我们的架构已经被划分为两个层(视图层和数据层),添加MVP会更自然些。我们只需要添加一个新的presenter层,然后将View中的部分代码转移到presenter就行了。
留下的Data Layer保持不变,只不过为了与这种模式保持一致性,它现在被叫做Model。
Presenter负责从Model中加载数据,然后当数据准备好之后调用View中相对应的方法。还负责订阅DataManager返回的Observable。所以,他们还需要处理schedulers和subscriptions。此外,它们还能分析错误代码或者在需要的情况下为数据流提供额外的操作。例如,如果我们需要过滤一些数据而且这个相同的过滤器是不可能被重用在其他地方的,这样的话在Presenter中实现比在DataManager中或许更有意义。
下面你将看到在Presenter中一个public方法将是什么样子。这段代码订阅我们在前一节中定义的dataManager.loadTodayPosts( )
所返回的Observable。
public void loadTodayPosts() {
mMvpView.showProgressIndicator(true);
mSubscription = mDataManager.loadTodayPosts().toList()
.observeOn(AndroidSchedulers.mainThread())
.subscribeOn(Schedulers.io())
.subscribe(new Subscriber<List<Post>>() {
@Override
public void onCompleted() {
mMvpView.showProgressIndicator(false);
}
@Override
public void onError(Throwable e) {
mMvpView.showProgressIndicator(false);
mMvpView.showError();
}
@Override
public void onNext(List<Post> postsList) {
mMvpView.showPosts(postsList);
}
});
}
mMvpView
是与Presenter一起协助的View组件。通常情况下是一个Activity
,Fragment
或者ViewGroup
的实例。
像之前的架构,View Layer持有标准的Framework组件,如ViewGroup,Fragment或者Activity。最主要的不同在于这些组件不再直接订阅Observable。取而代之的是通过实现MvpView接口,然后提供一些列简洁的方法函数,比如showError( )
或者showProgressIndicator( )
。这个View组件也负责处理用户交互,如点击事件和调用相应Presenter中的正确方法。例如,我有一个按钮用来加载博客列表,Activity将会在点击事件的监听中调用presenter.loadTodayPosts( )
如果你想看到一个完整的运用MVP基本架构的工作示例,可以从Github检出我们的Android Boilerplate project。也可以从这里阅读关于它的更多信息Ribot的架构指导
为什么这种架构更好?
Activity和Fragment变得非常轻量。他们唯一的职责就是建立/更新UI和处理用户事件。因此,他们变得更容易维护。
现在我们通过模拟View Layer可以很容易的编写出单元测试。之前这些代码是View Layer的一部分,所以我们很难对它进行单元测试。整个架构变得测试友好。
如果DataManager变得臃肿,我们可以通过转移一些代码到Presenter来缓解这个问题。
我们依然存在哪些问题?
值得一提的是它并不是一个完美的架构。事实上,不要天真的认为这是一个独特且完美的方案,能够解决你所有的问题。Android生态系统将保持快速发展的步伐,我们必须继续探索。不断地阅读和尝试,这样我们才能找到更好的方法来继续构建优秀的Android应用程序。
京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。
日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。
据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。
今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。
日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。
近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。
据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。
9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...
9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。
据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。
特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。
据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。
近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。
据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。
9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。
《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。
近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。
社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”
2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。
罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。