过去的一年,大量的内容被添加、被废弃或被删除,文档被更改,新的官方指导方针被引入等等。即使我以 Android 原生开发生态系统的的标准来看待这些问题,所发生的这些事情,都是非常疯狂的。当我开始思考这些内容的时候,我已经无法在我的脑海中描绘出一个完整的、详细的 Android 开发环境。
因此,我决定要花一些时间去整理这些内容,然后再来写这篇文章。本文中,我会**试图去总结 Android 原生开发的生态系统中发的事情,并且对原生开发未来的走向做一些预测。**我会将我的想法分成不同的章节去叙述,这些内容没有特定的顺序,但我会把最有争议的内容放在文章最后。
我希望我的这篇文章可以给你带来一些启发和帮助,但是你需要记住,本文不可能包含所有的内容,有可能会漏掉许多重要的观点,并且本文中的内容可能会包含我个人的一些偏见。
AndroidX
这个事情说起来有点儿疯狂,Google 官方在一年半前就发布了 AndroidX 的预览版本。并且在一年前, AndroidX 库就已经很稳定了,与此同时,Google 官方也宣布不再对遗留的库进行支持与开发。
用“稳定”来描述 AndroidX 这个库有点讽刺,现在关于 AndroidX 的任何东西都是不稳定的。Google 不断地在 AndroidX 下添加新的库和框架,使用 androidx 作为命名空间。许多“老”的 API(目前还不到一年)以非常快的速度发展。
到目前为止,我已经将两个应用程序迁移到了 AndroidX 上了。一切都很顺利,我已经不记得在这个过程中,带给了我多少的“惊喜“。Google 也提供了一个工具,Jetifier 可将依赖于支持库的库迁移为依赖于等效的 AndroidX 软件包,一个非常好用的工具。然而,即使是一个很小的工程,也不能实现“一键迁移”。
我也参与了没有迁移 AndroidX 的项目(项目并不计划迁移到 AndroidX), 现在也没有任何问题,所以,不迁移 AndroidX, 在有些情况下,也是一种可行的方案。
总而言之,**在新的 Android 项目中,建议直接使用 AndroidX。**并且,针对老项目,我也推荐你们将迁移到 AndroidX 列到计划中,虽然现在你看不到迁移 AndroidX 过后,带来的任何收益。无论如何,你都有可能在某个时间点进行 AndroidX 的迁移,所以最好能够按照自己的进度进行迁移,而不是在 6 个月后,你需要使用某个新的 AndroidX 库时,再进行紧急迁移。
Jetpack
在讨论 AndroidX 过后,还必须要提到 Jetpack。在我的印象中,Jetpack 开始是作为“架构组件”的一把保护伞推出的。但是到后面,引入了几乎所有关于 AndroidX 的 API。因此,现在来看,我们看不到它与 AndroidX 之间的任何区别,除了 Marketing 和 PR(即市场和公关)。
当你查看 Jetpack 主页[2]时,会发现,这个页面并不是一个技术文档页面。这个更像是一个早期的 SaaS 页面。
看看例子,开发者“赞誉”:
或者“信赖应用”列表:
这些在市场公关层面更受关注,如果 Jetpack 在 2020 年申请独立 IPO,我都不会感到惊讶。
不过,说真的,尝试向自己生态系统内的开发者“销售”API 的想法,我觉得存在一些问题,比如说,谁会想看搜索出来第一个就是 ViewModel 广告呢?
总而言之,Jetpack 只是 AndroidX 库的一个聚合,所以在前面写到的 AndroidX 的内容,在很大的程度上也适用于 Jetpack。在后面的内容中,我将单独讨论其中一些 API。
后台作业
在 Android 应用程序不在前台执行逻辑时,你可以有很多种方式来实现后台运行,这也是 Android 动态化的用例之一。如果你不引入 doze , SyncAdapter , CGMNetworkManager , FirebaseJobDispatcher , JobScheuler 和最近的 WorkManager,可以使用常规的启动服务(非绑定服务)来实现。这些都是 Google 提供的 API , 我可以说出所有的使用方式。当然,还有一些第三方库可以使用, 例如:Android-Job。
不过,Google 最近宣布,他们将围绕 WorkManager 来统一后台任务调度[3]。这听起来非常棒,我再也不用学习那么多后台调度的知识了,只是,不知道为什么,我好像以前在哪儿听到过这句话……
我们不管将来是否会统一使用 WorkManager,WorkManager 都还存在一些问题,比如可靠性。我不想在本文中解释为什么,但是请你一定要记住,如果你想在应用程序中使用 WorkManager,实现后台作业,一定要去读一下 dontkillmyapp.com 上的所有内容,并且要关注一下与 WorkManager 相关的 Google 问题列表[4]。
Android 的后台作业由于碎片化等原因,导致它们一团糟,而且还很不可靠。
在过去,我一直主张尽可能对数据和其它类型的后台处理进行同步。我或许是 SyncAdapter 最后的粉丝。今天,考虑到可靠性的问题,我建议,尽可能避免后台作业。如果你的老板一直坚持要使用这个功能,请将上面的链接发给他们,告诉他们,后台作业可能会需要数百小时的工作,才能实现,并且它带来的问题也会比它带来的好处多很多。
很多情况下,后台作业的需求是不可避免的。但是在大多数情况下,你都可以不使用它。虽然有时候会给用户带来一些不便,但是这可能是到目前为止的最佳方案。
数据库
在有关 SQLite ORMs 的内容里面,没有什么令人吃惊的内容—— Room 主宰着一切。Room 从 2.2.0 开始,添加增量注解解析支持。不过,请你一定要记住,你应用程序的架构不应该关心你使用的是什么样的 ORM 框架。说到 Room,“架构组件”也只是一个营销术语,并不是一个技术角色。
在 Android ORM 框架中,与 Room 竞争的主要是 SQLDelight。这是一个比 Room 老很多的库。但是据我所知,**在过去的一年里,它几乎被全部重写。****但新版本的 SQLDelight 只针对 Kotlin。**另一方面,SQLDelight 也支持 Kotlin Multiplatform,所以,随着 Kotlin 使用的增多,我预计, SQLDelight 的采用率也会随之增加。
顺便说一下,在 AndroidX 的命名空间下,也有 SQLite 的镜像。我不知道这个会有什么用处,但是如果你在应用程序中直接使用 SQLite,也可以对这个进行深入的研究。
此外,我们也不要忘记非关系型数据库,比如 Realm、Parse、Firebase、ObjectBox 等等 (其中一些,核心还是使用的 SQLite 来实现的),如果我没有记错的话,这些非关系型数据库中,大多数(甚至全部)都具有自动数据同步的功能。在之前有段时间里,它们非常地流行。但是现在,据我所知,已经不再流行了。也就是说,在短期内,我会持续关注非关系型数据库。
去年,我写了一个非常复杂的应用程序,对接了一个 Parse 的服务。这个 App 具有完全的离线支持,服务端本地化,用户指定的系统和语言设定,复杂的多媒体系统等功能。我在 Android 端上使用了 Parse 的 SDK。除了一些小的 WTF 以外,其他体验都非常棒。如果你们公司有很多后台开发人员,或者说你需要实现大量的服务端逻辑,这也许并不是最佳解决方案。但是对于仅仅只需要执行简单的 CRUD 操作的初创公司,这或许是一个不错的选择。
一个警告:如果你打算采用数据库即服务解决方案(如 Firebase),请你一定要关注长期使用的成本和影响。
外部存储
Android 外部存储发生了一个“很有意思”的变动。
如果,你在开发 App 的时候,把 Target API 调整为 29 及以上,之前获取 SD 卡文件的方法,现在都不能使用了,并且不会提示任何异常。现在,你需要使用 Android 存储访问框架来进行更细粒度的文件访问。不幸的是,Android 存储访问框架的工作原理与之前的读取方式完全不同,你可能需要对代码进行大量的重构,来实现新的文件访问和读取。
Google 原本希望在 Android 10 上,对所有的应用程序进行文件访问的限制,推广使用 Android 存储访问框架的方式进行文件访问。但是这个改动引起了社区内的强烈抗议,所以 Google 决定推迟推出这个功能。因此,现在即使你的应用程序在 Target API 29 及以上,也可以设置为在“Legacy”模式下可读取文件。不过,有可能在 Android 的下一个版本,不管你设置的 Target API 为多少,都会限制应用程序使用新的作用域访问模式。
到目前为止,我也还没有更新我应用程序的文件访问方式。但是从互联网上的讨论来看,实现新的文件访问方式是一项很具挑战的任务,虽然你的应用程序在“Legacy”模式下,没有任何异常,但是我建议你最好从现在开始,着手对代码进行重构和测试,以防发生不可控的事情。
Shared Preferences
在几周前,AndroidX 家族增加了一个新的库,这条提交信息写道:
新的库用来替换 SharedPreferences,新库的名称还没有确定下来,这次提交只是为了评审和设计文档(请自行申请设计文档)。
现在还没有什么值得担心的。不过,从长远来看,SharedPreferences 会被废弃掉,取而代之,使用新的库来实现类似的功能。
与 SharedPreferences 不同的是,这个新的库默认情况下使用的是异步的方式。换句话说,如果你需要取某个值,你需要实现回调,通过这个回调才能拿到值。
如果你对这种异步回调的原理感兴趣,你可以去看看 StackOverflow 上的这个回答。Reddit 的一个用户 Tolriq 分享了他们的 App 遇到的一个 SharedPreferences 的 Bug,这个问题影响了万分之一的月活用户。对于一般的应用程序来说,这个问题并没有什么明显的影响。但在一些需要高可靠性的应用上,就显得很不可靠了。举个例子,如果在 Android 汽车上,应用程序的无响应和崩溃会引起驾驶员的注意力被分散,这将有可能导致出现交通意外。
依赖注入
在依赖注入领域,最大的新闻莫过于 Dagger-Android 被弃用,有两点需要强调一下,首先我所说的弃用,不是正式的弃用,因为官方并没有发布声明。其次,Dagger-Android 并不是指整个 Dagger 2 框架,只是指其中相对比较新的部分。详细细节可以看我的另一篇文章[8]。
在 Android 领域也存在其他的依赖注入框架,但是我不认为他们会比 Dagger 更好。值得一提的是,Koin 是一个不错的依赖注入框架,但是我依然觉得它也不会引起多大的潮流。它之所以会被采用,无非是这两个原因,一个是,它拥有比 Dagger 好很多的文档,降低了很大的学习成本。第二个是它基于 Kotlin 进行编写,因为 Kotlin 的热度,给它也带来了不少的关注。到目前为止,Kotlin 的热潮几乎已经全部过去了,所以,我预测,Koin 的关注也将会逐渐减少。
不管这些框架如何发展,手动依赖注入的发展都会很缓慢。
Google 声称:随着应用程序的增大,手动依赖注入的成本会出现指数级增长。但是我并不这么认为,我觉得 Google 既不了解 “指数” 的含义,也没有实际去“衡量”过任何东西。这个申明的内容是错误的,我希望 Google 不再使用这种方式误导社区。
事实上,这种手动依赖注入在后端比较常见(尤其是微服务中,你并不想在每个服务中都添加对注入框架的依赖),也可以正常的工作。在后端,反射被经常用到,所以后端的依赖注入框架并不需要解析编译时的代码。
在 Android 上,解决方案与后端有一些不同,我们几乎不会用反射方案的依赖注入框架,所以就只剩下 Dagger 可以用了。其实,反射虽然会影响性能,但是在大多数项目,都是可以用的。我的意思并不是建议你们使用反射方案的依赖注入框架,这个选择并非是非黑即白的,你需要按照你的要求来进行选择。
无论如何,Android 领域上, Dagger 作为依赖注入框架的现行标准,我们所有人都在使用它。尽管 Google 在宣传上,对 Dagger 的使用成本使用漂亮的绿色图形进行展示,但是 Dagger 使用成本在实际上依然会随着时间增长而快速增长。越来越多的代码,在编译构建的时候需要花费更多的时间;你的开发人员越多,代码编译的次数就越多。当然,所有的开发人员都需要学习如何使用 Dagger , 这本身就是一项很大的成本。
换句话说,**虽然 Dagger 可以减少项目中编写的代码,但是需要花更多的时间去培训新人,在编译上花费更多的时候。**所以,对大型项目来说,使用 Dagger 会更耗时。
在一个大型项目中,编译耗时会逐渐成为生产力的瓶颈。当然, Dagger 也提供了很多优秀新的功能来帮助你优化编译时间,但前提是,你需要知道如何使用这些工具。读到这里,我相信你对手动依赖注入会很感兴趣。
数据绑定
作为一个 Android 开发者,都知道在写布局的时候,会经常调用 findViewById() 这个方法。DataBinding 诞生就是为了取代掉这个模板方法。老实说,在使用 findViewById 的时候,我并没有遇到过任何问题,虽然希望摆脱掉它,但我并不认为使用 DataBinding 就是一个更合理的方式。有一个好消息,很快我们就可以使用 ViewBinding 来摆脱 findViewById 了,也不需要使用 DataBinding。
说句实话,我不相信 DataBinding。对于它想解决的问题来说,这种方案在过于复杂。在使用 DataBinding 的时候,需要把代码逻辑放到 XML 布局中,这听起来很不错,但是经验丰富的开发人员都不会这么做,这个做法也是 Databinding 的另一个缺点。
早在 2016 年 11 月的时候,那个时候 Google 还在大肆宣传 DataBinding。我在 StackOverflow 的回答中作了如下预测:
我可以非常自信地预测:DataBinding 不会成为行业标准。DataBinding 可以带来短期收益,但是从长远来看,它将会使代码变得不可维护。一旦 Databinding 被长期使用,它的缺点就会暴露出来,将来它一定会被废弃掉。
我没有统计过使用 DataBinding 的项目,但是很明显,它没有成为行业标准。我从来没有在自己的项目中使用过它,也很少看到其他开发者使用。据我猜测,当 ViewBinding 逐渐成熟,并且被广泛采用,DataBinding 将会作为一个“传统”框架,大量地被引用到。
状态保存
自从引入 ViewModel 架构组件以来,在 Android 应用程序中,当配置发生更改,保存与恢复状态的逻辑,就变成了一个烂摊子,没有人去管理。虽然这样子说有点过分,但是我觉得,这已经是我最温和的表达方式了。
Gabor Varadi(又叫 Zhuinden)在 Reddit 论坛中描述了 ViewModel 引入带来的问题,我不需要再去写一遍了。再次强调,不推荐使用 onRetainCustomNonConfigurationInstance(),推荐使用 ViewModel。
在帖子的末尾,Gabor 作了一些预测:
你知道吗?Fragment 状态保存的方法已经被弃用了。
在我看来,废弃 Fragment 状态保存的方法是非常好的主意,众所周知, Fragment 的 onAttach() 和 onDetach() 方法就是为了支持状态保存的,现在废弃了状态保存的方法,那这两个方法也可以被废弃掉,并且这样子可以简化 Fragment 的生命周期。我长期以来都建议不保存 Fragments 的状态,忽略掉 onAttach 和 onDetach 方法,和我之前写的处理 Framgent 生命周期方法一致[12]。
尽管有很多理由表明,要废弃掉 Fragment 的状态保存,但是也不可能废弃掉 onRetainCustomNonConfigurationInstance(),这个可不是我说的,是 Jake Wharton 说的。在上面 Gabor 的帖子上,他的回复获得了最多的点赞。虽然我不太赞成 Jake 所说的话,但是我找不到更好的理由去说服自己。这个方法和 ViewModel 后台使用的原理完全一致,完全没有理由废弃掉它。
那我们应该怎么对待这些废弃的方法呢?Google 不管这些方法使用的技术方案和优势,都强制所有的 Andorid 应用迁移到 ViewModel。即使这些方案有可能优于 ViewModel 本身,他们也愿意放弃。听起来有点像是阴谋论吧。
我确实不喜欢保存非配置的状态,并且废弃掉对我没有任何影响,因为我从来都没有使用过它。事实上,大多数应用程序都不需要这些方法,当然,ViewModel 也不需要。我们需要处理状态改变的方法仅仅只有 onSaveInstanceState(Bundle) 这个方法。这个方法非常简单明了,可以同时处理保存和恢复的逻辑。所以,只要能用这种方式保存状态就可以了,我相信,我不是唯一一个使用这个方法的人。虽然 Google 对 ViewModel 进行了大量的营销宣传,但是对于很多经验丰富的开发者来说, ViewModel 还是太复杂了,我们有更简单有效的方法来处理状态存储。
如果 Google 别有用心,想强制所有项目使用 ViewModel , 那么它还将废弃掉 onSaveInstanceState(Bundle)方法。这听起来有点不可思议,如果将来真的这样发展,那说明我的基础理论是正确的。
考虑到 Android 的内存管理机制,Google 不可能在没有稳定的解决方案之前就废弃掉 onSaveInstanceState(Bundle) 。“幸运的是”,我们已经可以使用 ViewModel 来完成相关工作了。
我想,在一两年内,就能看到我的理论是否正确。
总而言之,如在本节开头所说,Android 状态保存将变成一个烂摊子。两年多前,我曾经写过 ViewModel 架构组件有害的文章[13]时,我就预测 ViewModel 会对保存与恢复状态的一点点造成影响。我所预测的都变成了现实,而且现在的情况比我曾经的预测更糟。
并发
在 Android 并发编程中,一个重要的 API 就是 AsyncTask, 不过它现在已经被弃用掉了。我之前已经写过很详细的文章分析过它了[14],在这里,将不再赘述。
下面我要说的内容,有可能会伤害很多读者,但是,请不要“恨”我。
**RxJava,是一个 Andorid 中常见的多线程框架。但是它现在将逐渐退出历史的舞台。**从 StackOverflow 的趋势图可以看出:
很多开发者对这个说法提出了质疑,他们反驳说这个数据不具有代表性,并且我们可以找到其它的理由来解释图上所发生的事情。他们所说可能是正确的,我个人本身也不是数据科学家。但是从图中我们可以看到,RxJava 与 AsnycTask 有相同的斜率。
**如果你没有时间去学习 RxJava 如何使用,并且你的项目中也没有使用过 RxJava,我建议你不要在你的项目中使用 RxJava。**事实上,我也一直不推荐使用 RxJava ,现在已经有数据支持我的这个观点了。
如果你的项目中使用了 RxJava,你也不用慌张,不需要紧急去重构你的项目。如果你的项目只有你一个人,或者整个项目组成员基本不会变动,保持项目现状就好了。但是你需要记住,**以后要招具有 RxJava 开发经验的人会越来越困难,新招开发人员可能需要学习使用 RxJava。**广泛使用 RxJava 的项目,在以后也会被认为"不酷",就像今天还在使用 AsyncTask 和 Loader 的项目一样。
我知道,很多 RxJava 的开发者都是 RxJava 骨灰级的粉丝,他们花了数周的时间去学习 RxJava,付出巨大的努力才说服队友在项目中使用 RxJava。现在我却在这里说 RxJava 已经过时了。我只能说,这不是我的个人意见,我只是对现有的情况进行分析,并根据我所看到的内容做出预测。我也有可能是错的。两军交战,不斩来使,请大家不要“攻击”我。
在 Kotlin 中,使用协程来实现多并发。最近使用协程实现了一些简单的用例,我发现它复杂、不稳定,甚至还有一些 Bug。
建议
当我们出去找工作,或者准备找工作的时候,我们一定要想,我面试的目标是什么,我自己的技术栈有哪些,近期能掌握的有哪些,我的哪些短板 ,列出来,有计划的去完成,别看前两天掘金一些大佬在驳来驳去 ,他们的观点是他们的,不要因为他们的观点,膨胀了自己,影响自己的学习节奏。基础很大程度决定你自己技术层次的厚度,你再熟练框架也好,也会比你便宜的,性价比高的替代,很现实的问题但也要有危机意识,当我们年级大了,有哪些亮点,与比我们经历更旺盛的年轻小工程师,竞争。
-
无论你现在水平怎么样一定要 持续学习 没有鸡汤,别人看起来的毫不费力,其实费了很大力,这四个字就是我的建议!!!!!!!!!
-
准备想说怎么样写简历,想象算了,我觉得,技术就是你最好的简历
-
我希望每一个努力生活的it工程师,都会得到自己想要的,因为我们很辛苦,我们应得的。
-
有什么问题想交流,欢迎给我私信,欢迎评论
【附】相关架构及资料
内含往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!
d高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter全方面的Android进阶实践技术**
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》,点击传送门,即可获取!