原文:The State of Native Android Development, November 2019原文:Vasiliy Zukanovandroid
翻译做者: ronaldong面试
Android原生开发的生态一直在不断地发展变化,过去5年从事android开发的经历让我深入的体会到了这一点。每隔2到3年,谷歌就会发布一些的新的开发指导建议、libraries、frameworks,我花了不少时间来认真审查这些变化并从中找出可能存在的问题。我相信许多Android开发者都有我这样相似的经历。数据库
然而,2019年绝对是Android原生开发生态发生剧变的一年。在这一年里,Android sdk添加了许多新的内容、重写和移除了一些旧的内容,官方的开发者指南也进行了大幅度的更新。想要对Android开发有一种完整而又详细的认识实在是太难了。后端
因而我写下了这篇文章,我试图去总结Android生态系统中所发生的事情,并对原生开发的将来作出一些预测。接下来我会把个人观点分红不一样的章节来进行具体的阐述,文章的最后我会分享一些极具争议的观点。api
我但愿这篇文章会对大家有所帮助,可是请记住,文章中确定遗漏了许多重要的内容,并且其中的许多观点都是我我的的偏见。安全
AndroidX的预览版是在一年半前发布的。大约一年前它变得稳定了,与此同时Google也中止了对旧版Support Library的进一步开发。当我写下这句话的那一刻,我想起了我以前在StackOverflow上提出的一个问题:Why does AOSP add new APIs to support libraries without adding them to SDK?,当时我是一个android开发新手,我想知道Support Library背后的动机,Googl为何不直接把Support Library放到android sdk里呢?服务器
不过使用“稳定”一词来描述AndroidX彷佛有点讽刺,由于关于AndroidX的任何信息彷佛都不是稳定的。Google不断的在往AndroidX里添加新的库和框架。命名空间和许多旧的API(目前还不到一年)正在以很是快的速度发展。多线程
到目前为止,我已经将个人两个应用迁移到了AndroidX。一切都很顺利,但我也不以为有多惊喜。Jetifier是一种将Support Library上的依赖项重定向到其AndroidX对等项的工具,转换效果使人惊讶。可是即便应用程序不大,也并非“一键式迁移”。架构
我还参与了一个还没有迁移到AndroidX的项目,没有任何问题。在某些状况下,彷佛我彻底不须要AndroidX。并发
总而言之,我想说的是:若是是开启新的Android项目,那么确定是应该使用AndroidX;对于现有的项目,我也建议您作好迁移到AndroidX的计划,即便您如今看不到明显的好处。不管如何,您极可能都须要迁移,所以最好按本身的计划进行迁移,而不是之后当你须要一些新的AndroidX库时进行紧急的迁移
在讨论完AndroidX以后,就不得不提到Jetpack了。据我所知,Jetpack最初只是“architecture components”的工具集合,可是后来扩展为包含了AndroidX的大多数(甚至全部)API的工具集合。所以,到目前为止,我尚未看到AndroidX和Jetpack之间有任何有意义的区别,市场营销和公关宣传除外。
当您访问Jetpack的官方网站时,它看起来不像是技术文档,更像是早期SaaS初创公司的主页。
看看这些“感言”:
再看看下面这些app:
若是Jetpack申请2020年独立IPO,我不会感到惊讶,由于他们是如此的专一于营销和公关。
不过说真的,这种向本身的生态系统中的开发人员“销售”api的作法存在一些深层次的问题。好比,为何有人真的想在搜索中宣传ViewModel?
总而言之,因为Jetpack的大部份内容都是来源于AndroidX,因此我以前写的有关AndroidX的内容在很大程度上也适用于Jetpack。
下面,我将分别讨论其中一些具体的API。
当应用程序不在前台时让应用也能执行操做是Android开发中最多见的场景之一。在引入doze模式、SyncAdapter、GCMNetworkManager、FirebaseJobDispatcher、JobScheduler以及最近的WorkManager以前,您能够经过启动服务(而不是绑定服务)来实现。这些都是Google本身的API,不过也有不少第三方解决方案,好比Android-Job。
可是,Google最近宣布,他们将经过WorkManager API来统一后台任务的调度。听起来很不错,可是因为某种缘由,当我听到这样的声音时,我总有种似曾相识的感受……
不管最终可否统一,WorkManager都没法解决在执行后台任务过程当中存在的一个最严重的问题:可靠性。我在这里不作解释,可是请记住,若是您须要在应用程序中执行后台任务,请先阅读dontkillmyapp.com上的全部信息。此外,请在Google的issue tracker中阅读并加注星标。
Android系统的后台任务执行与调度是一团糟,碎片化使得它很是细微且不可靠。
过去,我一直主张尽量的将数据同步等相似的工做放在后台来执行,我多是SyncAdapter的最后一批粉丝。可是今天,鉴于可靠性问题,我主张相反的作法:尽量避免在后台执行操做。若是您的PM坚持使用此功能,请向他们展现以上连接,并向他们解释,后台任务须要花费数百小时的时间来实现,并且带来的麻烦多于收益。
有些时候执行后台任务是不可避免的,可是在大多数状况下,您能够不这样作。即便以给用户带来一些不便为代价,它也多是最佳选择。
毫无疑问,Room在众多SQLite的ORM框架中占据着主导地位。从2.2.0开始,Room支持增量注解处理。不过请记住,您的应用架构不该过于关心使用了哪一种ORM框架。所以,做为architecture components的一员,Room只是市场术语,而不是技术角色。
Android开发中ORM框架的主要竞争者是SQLDelight。这个库比Room还要老,可是据我了解,在过去的一年左右的时间里,它已经被重写了不少。不幸的是,它如今只针对Kotlin。另外一方面,SQLDelight支持Kotlin跨平台。所以,随着Kotlin使用率的增长,我预计SQLDelight的使用率也会随之增长。
顺便说一下,AndroidX中有对原生SQLite的使用说明,我还不知道该怎么使用。可是若是您想在应用中使用原生SQLite,那么你或许须要去认真的研究下这个主题。
此外还有许多针对Android的非关系型的数据库,例如Realm,Parse,Firebase,ObjectBox等(其中有些仍在使用SQLite)。若是我没记错的话,它们中的大多数(甚至所有)都具备自动数据同步功能。一段时间以来,这些解决方案比较流行,但据我所知,它们已经不复存在了。可是我并不会立刻认为非关系型的数据库再也不重要了。
去年,我编写了一个很是复杂的集成了Parse Server的Android应用。我使用了Android版本的的Parse SDK,体验都很是好。若是您的公司已经雇用了许多后端开发人员,或者您须要实现许多服务器端逻辑,这可能不是最佳解决方案,可是对于仅在后端执行CRUD操做的初创企业和我的来讲,这可能会是一种好的选择。
可是我必须提醒的一点是:若是您要采用数据库即服务的解决方案(例如Firebase),那么请务必了解其长期的成本和影响。
关于外部存储的开发,这里有许多有“意思”的事情。
若是您应用的target sdk版本等于或者大于29,那么你的应用将没法再正常访问手机外部存储上的文件,除了少数几种明显的状况。相反,您须要使用SAF框架(听说),该框架容许用户进行更精细的访问管理。不幸的是,SAF的工做方式与以前彻底不一样,所以某些应用程序可能须要进行重大重构。
Google但愿从Android 10开始对全部的应用程序都实行这一要求,但它引发了开发者社区的强烈抗议,因而他们决定推迟此功能。所以,即便您的应用设置target sdk版本为 29,它仍能够在“旧版”模式下工做。可是,不管目标API级别是多少,下一版的Android系统都将对全部应用的存储访问范围作更加严格的限制。
到目前为止,我尚未使用SAF框架,可是从我在互联网上阅读的许多讨论中看来,这多是一项艰巨的任务。所以,若是您的应用程序还在以“旧版”模式使用外部存储,那么最好当即开始进行重构和测试。
几周前,AndroidX系列中添加了一个新框架。它的commit message是这么说的:
New library meant to replace SharedPreferences. The name is not final, this is just for implementation review and to support the design doc (feel free to request the design doc privately)[…]
目前咱们无需担忧,但从长远来看,彷佛SharedPreferences会被重写,咱们须要使用这种新的方法。
SharedPreferences和这个新框架之间的主要区别在于,默认状况下后者是异步的。换句话说,您须要实现一个回调以获取特定键的值,该回调将在之后的某个时间收到通知。
若是您对这种异步通知的机制感到好奇,则能够阅读StackOverflow上的这个答案。Reddit用户Tolriq在这里分享了他们遇到此bug的几率。在他们的应用中,这个bug会影响1 / 10,000 / SESSIONS_PER_USER_PER_MONTH的用户。对于通常的应用程序,这可能微不足道。可是在须要高可靠性的状况下,这可能会引发严重的后果。例如,在装有Android Auto的汽车中,应用程序挂起和随后的崩溃会分散驾驶员的注意力,这可能会致使很是不幸的后果。
在依赖注入方面,最大的变化就是Dagger-Android的弃用。这里我想解释两点:首先,我说的弃用并非指“正式”弃用,由于它还没有正式弃用。其次,Dagger-Android并非整个Dagger2框架,而只是相对较新的功能。我在这个主题上写了一篇很是详尽的文章,因此我在这里再也不重复。
至于其余依赖注入框架,我不认为它们是Dagger的真正竞争者。例如,Koin也许不错,但我认为它不会吸引不少人。实际上,我相信它仅因为两个主要缘由而获得了初步采用。第一个是Dagger的糟糕文档,Koin在这方面要比Dagger领先N光年。第二个缘由是Koin是用Kotlin编写的,它借着kotlin发展的浪潮开始兴起。到目前为止,这波浪潮已经几乎消逝。
我认为可能会发生的状况是,纯依赖注入的框架(又称为手动依赖注入)的会逐渐出现。
如今,谷歌声称“随着应用程序的不断增大,手动依赖项注入成本呈指数增加”。我认为,这仅代表他们既不了解“指数”的含义,也没作过任何实际的“测量”。此声明是彻底错误的,我但愿Google不要以这种方式来误导社区里的开发者了。
事实上,纯依赖注入在后端开发中很是广泛(尤为是在开发微服务的时候,您不想在其中添加对每一个服务的框架的依赖),反射也是后端开发中的一个有效的选项。所以,若是要使用依赖注入框架,他们一般不须要解析编译时代码。
可是,Android开发的状况有所不一样。因为咱们不能使用反射式DI框架,因此咱们使用了Dagger。事实上,咱们可使用反射式DI框架,而且对于大多数项目来讲均可以,可是却存在性能问题。我并非说使用反射式DI框架是安全的,但它绝对不是一种非黑即白的方案。不管如何,Dagger已是在Android开发中使用依赖注入的事实上的标准,咱们都使用它。但使用Dagger的代价也很明显:
换句话说,虽然Dagger确实容许您编写更少的代码,但因为它会影响构建时间和所需的培训时间,所以在大型项目上它会花费更多的时间。
在大型项目中,构建时间慢才是真正的问题,并成为主要的生产力瓶颈。所以,尽管Dagger确实提供了很是出色的功能来简化DI(固然,一旦您知道如何使用它),但我相信咱们对纯依赖注入会产生愈来愈多的兴趣。
开发人员采用DataBidning的主要缘由之一是再也不须要调用findViewById()了。老实说,findViewById确实很冗余,我也不介意摆脱它们。可是,在我看来,调用findViewById()带来的小麻烦并不能证实使用DataBinding是合理的。好消息是,很快咱们将可以使用另外一个新功能ViewBinding来删除这些findViewById()的调用。
实际上,我历来都不相信DataBinding。对于它(应该)解决的问题,我感受太复杂了。此外,DataBinding容许开发人员将逻辑放入XML布局中。经验丰富的开发人员是不会使用这种方法的,由于这增长了项目维护的难度。这是DataBinding框架的另外一个缺点。
早在2016年11月,当DataBinding正处于大肆宣传的顶峰时,我在StackOverflow上的一个答案中作出了如下预测:
However, there is one prediction I can make with a high degree of confidence: Usage of the Data Binding library will not become an industry standard. I’m confident to say that because the Data Binding library (in its current implementation) provides short-term productivity gains and some kind of architectural guideline, but it will make the code non-maintainable in the long run. Once long-term effects of this library will surface – it will be abandoned.
如今,关于DataBinding的使用率,我没有任何统计数据,可是很明显,它并无成为行业标准。我本身还从未见过使用DataBinding的专业项目,也不多见到在其应用中使用DataBinding的开发人员。据我估计,一旦ViewBinding成熟并被普遍采用,DataBinding将会更加流行,并成为“传统”框架。
自从引入ViewModel以后,在Android应用中对于配置更改的处理就变得一团糟。我知道个人这种说法太苛刻了点,但实际上,这是我能够描述的最温和的表达方式。
对我来讲幸运的是,Gabor Varadi(又名Zhuinden)已经在Reddit上的这篇文章中对这一问题进行了总结,因此我不须要本身作。他的结论就是:不推荐使用onRetainCustomNonConfigurationInstance(),而推荐使用ViewModel。有趣的是,在该帖子的结尾,Gabor作了一些颇具嘲讽味道的预测:
你发现什么了吗?Retained Fragments如今已经被弃用了! 。
我认为,弃用Retained Fragments其实是一个好主意。Fragment的生命周期里具备onAttach()和onDetach()这两个方法的惟一缘由就是为了支持Retained Fragments的使用。经过弃用Retained Fragments,这些方法也能够弃用,而且能够简化Fragment的生命周期。若是您使用个人方法来处理Fragment的生命周期,那么这种弃用就不会让您感到困扰,由于我长期以来一直建议您避免Retained Fragments,忽略onAttach()和onDetach()方法。
尽管有充分的理由要弃用Retained Fragments,但弃用onRetainCustomNonConfigurationInstance()倒是胡说八道。这不是我说的,而是Jake Wharton说的(您能够在前面提到的Gabor在Reddit上的帖子下阅读他的原话)。
为何要作这些变更呢?我只能看到一种解释:Google决定无论其它技术优点如何,都强制将全部Android项目迁移到ViewModel。他们愿意弃用全部现有的替代方案以实现其目标,即便这些替代方案实际上优于ViewModel自己。
听起来有点阴谋吧?我赞成。可是,幸运的是,咱们能够对此理论有一个简单的检验。
虽然我不喜欢Preserving State on Configuration Changes,但它不会以任何方式影响我,由于我没有使用它。实际上,绝大部分应用程序都不须要它。它们也不须要ViewModel。正确处理Configuration Changes的方式就是在onSaveInstanceState(Bundle)回调方法里增长处理逻辑。这是一种更简单,更好的方法,由于它还能够处理保存和恢复流程(也称为进程终止)。所以,只要我能以这种方式保存状态,就能够了。尽管Google进行了大量的营销和公关工做,但许多经验丰富的开发人员都意识到ViewModel太复杂了,而且有更好的方法来保留配置更改的状态。
所以,若是Google确实有别有用心,而且想迫使全部项目都使用ViewModel,那么他们还须要弃用onSaveInstanceState(Bundle)。我知道这听起来很疯狂,但这其实是件好事,由于若是这种疯狂的预测成真,您就会知道基础理论是正确的。
可是,鉴于Android的内存管理机制,Google不能仅在不提供可靠替代方案的状况下就弃用onSaveInstanceState(Bundle)。“幸运”的是,这些变化已经应用在ViewModel的保存状态模块上了。
我想在一两年内咱们就会知道这种作法是否有任何优势。‘
总而言之,正如我在本节开头所说的那样,自ViewModel发布以来,Android中的Configuration Changes就成了屎。两年多之前,当我撰写题为“Android ViewModel Architecture Component Considered Harmful”的文章时,我预测ViewModels将是一种浪费。个人全部预测都是真实的,但不幸的是,事实证实真相比这还糟。
在并发这方面,最大的变化就是AsyncTask的弃用。我已经写了一篇有关此主题的很是详细的文章,并提出了具体建议,所以在此再也不赘述。
接下来我说的话可能会使部分读者感到不满。拜托,别太把这事当真。
Android开发中另外一个流行的多线程框架RxJava很快就会成为“过去式”。从下面这幅StackOverflow趋势图能够明显看出:
许多开发者会质疑个人观点,称该数据不具备表明性,而且还有其余方法的能够解释该图。他们多是正确的,由于我本身也不是数据科学家。可是,在此图中,我看不到任何其余关于峰值的解释,并且RxJava的曲线与AsyncTask的曲线具备相同的斜率。
所以,若是您还没有花时间在学习RxJava上而且您的项目没有使用它,那么我建议您避免使用它。实际上,这一直是个人建议,今天它也获得了数据的支持。
若是您的项目已经使用了Rx,也请不要惊慌,您无需当即重构任何东西。可是,请记住,从此找到具备Rx经验的开发人员将愈来愈困难。所以,在项目中普遍使用Rx可能须要新的开发人员投入更多的时间。最终,普遍使用Rx的项目将被视为“not cool”(例现在天使用AsyncTask和Loaders的项目)。
我知道个人这些观点对于许多开发人员来讲很不友好。他们花了数周时间来学习RxJava,甚至说服了同事在项目中使用RxJava,如今我却说它会成为“过去式”。我只想说我只是分析实际状况并根据我所看到的作出预测,我多是错的,也多是对的。
在Kotlin语言中,咱们可使用协程。我最近使用协程实现了一些复杂的用例,发现此框架很是的细微和复杂,而且相对不成熟,我甚至发现了一个bug。
有一种流行的说法是,协程使得并发处理更简单。我历来不这样认为,由于我知道并发是很是复杂的,可是在我有了一些实践经验以后,我能够自信地说,协程并无想像中的那么美好。在我看来,协程实际上增长了程序复杂性,因此我建议大家当心地使用它们。
另外一方面,协程彷佛将成为Kotlin语言里处理并发操做的默认方式。所以,我认为若是您编写Kotlin代码,您须要投入时间并学会使用它们。
据我所知,目前还有一个流式框架,它在协程之上添加了流处理操做符。几个月前才稳定下来,因此我如今还不能说什么。
如今让咱们来讨论一下Kotlin。根据以往的经验,我知道这是一个很是敏感的话题,并且无论我描述的多么客观,最终都会遭受一些开发者的攻击。然而,我认为在总结原生Android开发现状的时候跳过Kotlin是极不诚实的。所以,我再次请你不要把我说的话当真。
你所须要知道的一个重要的事实是:在Android开发中使用Kotlin会严重增长你的构建时间。
在这篇文章中,您会了解到我在使用Kotlin进行开发时对构建时间所进行的统计测试的结果。clean build 增长了18%的构建时间,incremental build 增长了8%的构建时间。
Uber与JetBrains也联合发表了他们本身的研究结果,他们的结果更为负面。若是您不在应用程序中使用注解处理器,那么引入Kotlin可能会使您的构建时间增长四倍!若是您使用了注解处理器,那么引入Kotlin会使您的构建时间增长50%-100%。
Uber的研究结果与将OkHttp迁移到Kotlin版本后构建时间增长了4倍的结果是一致的。
若是您对这些数字感到惊讶,您不用担忧-这不是您的错,并且您并不孤单。尽管这个事实极为重要,但它并未获得普遍讨论,而且我以为Google也试图回避这个事实。我曾与Google内部一个熟悉此事的开发人员有过一次很是有趣的讨论,我问他是否能够讨论下这个话题,他说:“我不喜欢;我不喜欢;我不喜欢。这是一件很微妙的事情。”
除了增长构建时间以外,Kotlin还不支持增量注解处理,而在大约10个月前Java就已经支持增量注解处理了。
两年前,我写了一篇文章来警告开发者们在早期使用Kotlin时可能会遇到的潜在风险。在很长一段时间内我被称为“kotlin的讨厌者”。
可是,若是您今天阅读这篇文章,您会发现我实际上低估了这些问题的严重性。在大型的Android项目上,构建时间是最糟糕的生产力杀手之一,并且即便在今天,即Kotlin被官方“正式采用”两年多以后的今天,Kotlin仍然不如Java。无论Kotlin带来什么其余好处,全部这些均可能因为更长的构建时间而被否认。
也就是说,咱们不该改忽视这样一个事实:是谷歌将android开发的生态强行推向了kotlin,使得其使用率在稳步上升。
就我我的而言,我并无在我目前已经开始的新项目中选择kotlin语言,我不想在kotlin上浪费我本身的时间。不过,从如今开始,我会认真考虑使用kotlin来开发新项目,我已经在几个demo上尝试过了。可是我不一样意开发人员说你必须在新项目中使用Kotlin,这仍然是一种权衡。
至于大家是否应该将现有项目迁移到Kotlin,我没法提供任何通常性建议,您须要根据具体的状况进行仔细的分析。可是,若是您确实决定开始(或已经开始)迁移,那么这个帖子可能会对您有用。
在过去的两年中,我开发了三个新的应用程序。我认真研究了现有的项目并分析了早期技术决策所带来的长期影响。我写了一些博客,提供有关Android开发的高级课程。我花了不少时间在互联网上讨论Android开发相关主题。
尽管如此,我仍是感受本身没法跟上Android生态系统的变化。
若是是这样的话,对于那些缺少经验、须要指导的Android开发人员,我深表歉意,并且我至今没法想象从头开始学习Android开发的感受。当您对框架和工具感到满意的时候,其中许多将已过期或即将过期。加入这个本来很棒的社区多是最糟糕的时刻。Google为他们的“包容性”感到很是自豪,但看起来它不适用于经验不足的开发新手。
我我的认为Google对Android框架所作的更改会致使巨大的人类潜力浪费。阅读全部这些更改须要花费数小时,更不用说实际实施它们了。我宁愿花更多的时间来创造价值,而不是追逐本身的尾巴。
在这篇文章中,我试图总结有关Android原生开发现状的一些重要的内容,我还对将来作了一些预测。这篇文章并不完美:它可能包含一些错误,并且还错过了一些其余重要内容。请随时在下面的评论中纠正我。可是请记住,本文没有任何私人内容。我知道我提出了一些很是有争议的观点,可是我相信这是对的。
我还在本文的多个地方引用了我以前写的一些文章。我这样作并非为了炫耀并说“看,我是正确的!”,而是让您可以了解我过去的预测并将其与实际发生的状况进行比较。当我写这些文章时,他们读起来就像您今天读本篇文章同样疯狂。可是我所作的预测却很是准确。
固然,我也想说:“看,我是对的!”。我冒着巨大的专业风险发表了这些有争议的预测,在得知本身没有误导读者后我感到很是的欣慰。即便有时候我宁愿本身是错的,也但愿Google成为真正的合做伙伴。可是到目前为止,状况并不是如此。
最后附上文章做者对于跨平台开发的一些见解,仅供参考。
在这里小编也分享一份私货,本身收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料帮助你们学习提高进阶,也节省你们在网上搜索资料的时间来学习,也能够分享给身边好友一块儿学习
若是你有须要的话,能够点赞+评论,关注我, 加Vx:15388039515(备注思否,须要进阶资料)