58APP Android构建工具升级历程

背景

因为58APP是一个比较庞大的工程,涉及业务线较多,除非特殊状况58APP每一年只会有一次统一升级各SDK的计划,包括第三方SDK,构建环境等。本次就介绍一下升级Android构建工具的历程。html

Android APP的构建通常涉及了三个工具,Android plugin、Gradle、Build Tools。58APP也不例外,在依赖于这三个工具同时咱们也有自定义的打包插件,自定义插件用于在构建时对构建过程进行自定义,构建过程当中运行一段咱们的代码,从而达到咱们的目的。8App中这三个工具的版本分别以下:java

94DF1576-9793-4A3F-9193-7C8626627FF4.png

其中最主要的版本号为Android plugin,其余两个工具的版本号根据Android plugin的版本号的依赖而决定。Android plugin只规定了Gradle和Build Tools的最低版本,所以理论上咱们能够直接升级Gradle和Build Tools的版本号。而对打包效率功能影响最大的是Android plugin,所以不能随意升级,必须通过详细调研。android

目前Android plugin版本为3.0,该版本包含了各类解决大型项目性能问题的更改,具体变动此处再也不赘述。git

Android plugin版本变化梳理

最新的Android plugin版本为3.3.1(笔者升级时为最新版本,目前已更新至3.4.x),Android plugin版本号命名的规则大体以下:github

  1. 版本号一共为3位
  2. 当发生重大升级时更新第一位版本号
  3. 当发生较大升级时更新第二位版本号
  4. 当修复bug或者其余的一些补丁时更新第三位版本号

所以3.3.1为3.3.0版本的小版本升级,基本与3.3.0一致。因为3.0到3.3.0只发生了3个较大版本升级,所以咱们来分别分析一些这三个较大版本升级的变化。apache

3.1.0 (2018.3)

依赖状况:canvas

CCCF276C-CE71-4AAC-A7EA-4C1A7FD123AB.png

新的Dex编译器D8

默认状况下,Android Studio如今使用名为D8的新DEX编译器。 DEX编译是将.class字节码转换为Android Runtime(或Dalvik)的.dex字节码的过程。 与以前的编译器(称为DX)相比,D8编译速度更快,输出更小的DEX文件,同时具备相同或更好的应用运行时性能。api

D8不会影响正常的开发流程,若是不须要能够配置如下代码关闭D8编译器:缓存

android.enableD8=false

其余行为变化

  • 在构建多个平台不一样ABI的APK时,默认状况下,插件再也不为如下ABI生成APK:mips,mips64和armeabi。若是要构建以这些ABI为目标的APK,则必须使用NDK r16b或更低版本并在build.gradle文件中指定ABI,以下所示:

5861A858-27FF-4FC0-A8BA-F60F6E0FD4D8.png

  • 在为Android Instant App构建配置APK时,语言配置拆分如今默认按根语言分组。 例如,若是您的应用包含zh-TW或zh-CN语言环境的资源,Gradle将以zh语言配置拆分打包这些资源。 您可使用include属性定义本身的组来覆盖此行为,以下所示:

03258DBA-AE62-404A-8DC0-D49B8CB34596.png

  • Android插件的构建缓存如今去除超过30天的缓存条目。
  • 将“auto”传递给resConfig再也不自动选择要打包到APK中的字符串资源。 若是继续使用“auto”,插件将打包您的应用及其依赖项提供的全部字符串资源。 所以,您应该指定但愿插件打包到APK中的每一个区域设置。
  • 因为本地模块不能依赖于应用程序的测试APK,所以使用androidTestApi配置而不是androidTestImplementation将依赖项添加到已检测的测试中会致使Gradle发出如下警告:

BB8831A3-C715-4747-B928-C264E423099B.png

Bug修复

  • 修复了Android Studio没法正确识别复合构建中的依赖项的问题。
  • 修复了在单个构建中屡次加载Android插件时出现项目同步错误的问题 - 例如,当多个子项目在其buildscript类路径中包含Android插件时。

3.2.0 (2018.9)

依赖状况:

A64F9B24-CB82-41F4-9FB8-3CC84E74A917.png

新功能

  • 支持构建Android应用程序包:应用程序包是一种新的上传格式,包括全部应用程序的已编译代码和资源,同时推迟APK生成并签名到Google Play商店。 您再也不须要构建,签名和管理多个APK,而且用户能够得到针对其设备进行优化的较小下载。 要了解更多信息,请阅读关于Android App Bundles
  • 使用注解处理器时支持提升增量构建速度:AnnotationProcessorOptions DSL如今扩展了CommandLineArgumentProvider,它使您或注释处理器做者可以使用增量构建属性类型注释来注释处理器的参数。 使用这些注释能够提升增量和缓存清理构建的正确性和性能。 要了解更多信息,请阅读注释处理器的Pass参数
  • AndroidX的迁移工具:当使用Android Gradle插件3.2.0和Android 3.2及更高版本时,您能够经过从菜单栏中选择Refactor> Migrate to AndroidX来迁移项目的本地和Maven依赖项以使用新的AndroidX库。 使用此迁移工具还会在gradle.properties文件中将如下标志设置为true:性能优化

    • android.useAndroidX:设置为true时,Android插件使用相应的AndroidX库而不是支持库。 若是未指定此标志,则默认状况下插件会将其设置为false。
    • android.enableJetifier:当设置为true时,Android插件会自动迁移现有的第三方库,经过重写其二进制文件来使用AndroidX。 若是未指定此标志,则默认状况下插件会将其设置为false。 只有当android.useAndroidX也设置为true时,才能将此标志设置为true,不然会出现构建错误。
  • 新的代码缩减器,R8:R8是一种用于代码缩减和混淆的新工具,它取代了ProGuard。 您能够经过在项目的gradle.properties文件中包含如下内容来开始使用R8的预览版本:

    android.enableR8 = true

其余行为变化

  • 如今默认状况下启用D8 Desugaring。
  • AAPT2如今在Google的Maven仓库中。 要使用AAPT2,请确保build.gradle文件中包含google()依赖项,以下所示:

E193CC63-7F68-46E3-86EE-95498CCB9A49.png

  • 如今默认启用本机multidex。 在将应用程序的调试版本部署到运行Android API级别21或更高级别的设备时,之前版本的Android Studio启用了本机multidex。 如今,不管您是部署到设备仍是构建APK以进行发布,Android Gradle插件均可觉得设置minSdkVersion = 21或更高版本的全部模块启用原生multidex。
  • 该插件如今强制执行依赖protobuf插件(0.8.6),Kotlin插件(1.2.50)和Crashlytics插件(1.25.4)的最低版本。
  • 功能模块插件com.android.feature如今在指定模块名称时仅强制使用字母,数字和下划线。 例如,若是您的功能模块名称包含破折号,则会出现构建错误。 此行为与动态要素模块插件的行为相匹配。

Bug 修复

  • JavaCompile如今能够在具备数据绑定的项目中缓存。
  • 更好地编译避免库模块使用数据绑定。
  • 若是因为某些不可预测的构建错误而在早期版本中禁用了按需配置,则如今能够从新启用按需配置。(3.0.x or 3.1.x在Gradle 4.6及以上时按需配置不能使用)

3.3.0 (2019.2)

依赖状况:

07C8228E-808A-4F48-B229-01B4C60DF8F5.png

新功能

  • 改进的类路径同步:在解析运行时和编译时类路径的依赖关系时,Android Gradle插件会尝试修复出如今多个类路径中的依赖关系的某些下游版本冲突。

    例如,若是运行时类路径包含库A版本2.0而且编译类路径包含库A版本1.0,则插件会自动将编译类路径的依赖性更新为库A版本2.0以免错误。

    可是,若是运行时类路径包含库A版本1.0而且编译包含库A版本2.0,则插件不会将编译类路径上的依赖项降级为库A版本1.0,而且您将收到错误。 要了解更多信息,请参阅修复类路径之间的冲突。

  • 使用注解处理器时改进了增量Java编译:此更新经过在使用注释处理器时改进对增量Java编译的支持来减小构建时间。

    • 对于使用Kapt(大多数仅限Kotlin项目和Kotlin-Java混合项目)的项目:即便使用数据绑定或retro-lambda插件,也会启用增量Java编译。 Kapt任务的注释处理还没有增量。
    • 对于不使用Kapt的项目(仅Java项目):若是您使用的注释处理器都支持增量注释处理,则默认状况下会启用增量Java编译。 要监控增量注释处理器的采用状况,请观看Gradle issue 5277。

      可是,若是一个或多个注释处理器不支持增量构建,则不会启用增量Java编译。 相反,您能够在gradle.properties文件中包含如下标志:

      android.enableSeparateAnnotationProcessing=true

      当您包含此标志时,Android Gradle插件会在单独的任务中执行注释处理器,并容许Java编译任务以递增方式运行。

  • 使用过期的API时更好的调试信息:当插件检测到您使用的API再也不受支持时,它如今能够提供更详细的信息,以帮助您肯定API的使用位置。 要查看其余信息,您须要在项目的gradle.properties文件中包含如下内容:

    android.debug.obsoleteApi=true

    您还能够经过从命令行传递-Pandroid.debug.obsoleteApi = true来启用该标志。

  • 您能够从命令行对动态要素模块运行检测测试。

其余行为变化

  • 延迟任务配置:该插件如今使用Gradle的新任务建立API来避免初始化和配置完成当前构建不须要的任务(或不在执行任务图上的任务)。 例如,若是您有多个构建变体,例如“发布”和“调试”构建变体,而且您正在构建应用程序的“调试”版本,则该插件可避免初始化和配置“发布”版本的任务。

    在Variants API中调用某些旧方法(例如variant.getJavaCompile())可能仍会强制执行任务配置。 要确保您的构建针对延迟任务配置进行了优化,请调用新方法,而不是返回TaskProvider对象,例如variant.getJavaCompileProvider()。

    若是您执行自定义构建任务,请了解如何适应Gradle的新任务建立API。

  • 对于给定的构建类型,当设置useProguard为false时,插件如今使用R8而不是ProGuard来缩小和混淆应用程序的代码和资源。 要了解有关R8的更多信息,请阅读Android Developer's Blog中的此博客文章
  • 库项目的R类生成更快:之前,Android Gradle插件会为每一个项目的依赖项生成一个R.java文件,而后与应用程序的其余类并行编译这些R类。 该插件如今直接生成包含应用程序编译的R类的JAR,而无需先构建中间的R.java类。 此优化能够显着提升包含许多库子项目和依赖项的项目的构建性能,并提升Android Studio中的索引速度。
  • 该插件强制指定某些第三方插件的最低版本。
  • 单变量项目同步:将项目与构建配置同步是让Android Studio了解项目结构的重要一步。 可是,对于大型项目而言,此过程可能很是耗时。 若是您的项目使用多个构建变体,您如今能够经过将项目同步限制为仅当前选择的变体来优化项目同步。

    您须要在Android Gradle Plugin 3.3.0或更高版本中使用Android Studio 3.3或更高版原本启用此优化。 知足这些要求后,IDE会在您同步项目时提示您启用此优化。 默认状况下,新项目也会启用优化。

    要手动启用此优化,请单击文件>设置>实验> Gradle(Android Studio>首选项>实验> Mac上的Gradle),而后选中Only sync the active variant复选框。

  • 自动下载缺乏的SDK包:此功能已扩展为支持NDK。 要了解更多信息,请阅读使用Gradle自动下载缺乏的软件包

Bug修复

  • 尽管Jetifier已启用,但构建过程调用android.support.v8.renderscript.RenderScript而不是AndroidX版本
  • 由androidx-rs.jar引发的冲突包括静态捆绑的annotation.AnyRes
  • 使用RenderScript时,您再也不须要在build.gradle文件中手动设置Build Tools版本

升级动力

Dex编译器D8

3.1.0版本新增了名为D8的Dex编译器,听说D8编译速度更快,输出更小的DEX文件,同时具备相同或更好的应用运行时性能。

代码缩减和混淆的新工具R8

R8是一种用于代码缩减和混淆的新工具,它取代了ProGuard。目前处于预览版,默认不启用。

根据官方测试数据,启用R8与D8后,编译速度有所提高,可是dex文件减少并不明显,详细见下图所示:

3863B659-F545-45F0-B3E7-9FD20E295FCA.png

010092E5-13B4-423B-A5D1-0CFA65A2B9A5.png

000CF6AC-3ACA-4E3A-ABB1-071FB1B99CC9.png

因为R8处于预览版,并且对减小包大小的效果并不明显所以本次升级并不启用R8。

其余性能优化

  • 使用注解处理器时支持提升增量构建速度。
  • 延迟任务配置,避免初始化和配置完成当前构建不须要的任务。
  • 库项目的R类生成更快,3.3版本插件直接生成包含应用程序编译的R类的JAR,而无需先构建中间的R.java类。
  • 单变量项目同步:Android Studio 3.3与3.3版本插件配合时优化了项目同步功能,项目使用多个构建变体时,能够经过将项目同步限制为仅当前选择的变体来优化项目同步速度。

开始升级

gradle语法变动

  1. 错误:插件3.1版本开始instrumentTest修改成androidTest,影响全部包含instrumentTest语法的库
  2. 警告:android.enableAapt2=true属性已失效,默认使用aapt2
  3. 警告:android.enableDesugar=false属性已失效,默认true
  4. 依赖语法变动:

    2B03C99A-29EF-49C8-A625-9D748800C6B7.png

multidex问题

  1. 生成的maindexlist文件目录以及名称修改,致使自定义multidex.gradle中的hook失效,提示 multiDexBuildMainlistTask can not find the maindexlist.txt file。因为代码中经过名称获取目录,所以将判断的名称由maindexlist.txt改成mainDexList.txt解决此问题,代码以下:

    B2A62CA8-D92B-4DAC-928E-3C54ADCBFF56.png

  2. 打包时提示maindex field超出,使用dex-member-list工具分析,发现结果以下:

    BEB59020-AD65-41FB-A5EF-4CC2DEEDEF79.png

    再经过android-classyshark分析,所有是各个包下的R文件超出,所以添加如下一些不须要的R文件至main-exclude-libs-list.txt

    A1487170-8A94-4DD0-A5A4-DD03CCFFE8D8.png

API问题:

  1. junit.framework.Assert类不存在,因为使用的地方都是断言,所以进行相似如下修改:

    E221056D-AA88-43DE-B734-774A0B754EAB.png

  2. canvas.save(Canvas.ALL_SAVE_FLAG);方法不存在,仅存在无参方法canvas.save(),所以修改成canvas.save()
  3. canvas类中除ALL_SAVE_FLAG之外的其余FLAG都不可用了,所以涉及到canvas.saveLayer方法中的FLAG参数大部分是错误的,修改成ALL_SAVE_FLAG,源码的实现中此方法的saveFlags也会被替换为ALL_SAVE_FLAG

    2EAF20BA-AE5F-4D0F-A8A3-A828B39B26AA.png

  4. java.lang.NoClassDefFoundError: Failed resolution of: Lorg/apache/http/message/BasicNameValuePair httpclient相关类找不到,google发现新增一个配置在manifest文件
    3234CF61-8D4A-4052-AA5F-AF2EAAC9F377.png
  5. Permission Denial: startForeground from pid=2381, uid=10351 requires android.permission.FOREGROUND_SERVICE 新增权限android.permission.FOREGROUND_SERVICE。因为项目中只有一处com.wuba.debug.floatball.FloatBallService中使用startForeground,并且此类已再也不使用,所以删除此类。

jar包资源问题

一些不规范的jar包中存在res资源目录(如百度地图sdk),在Android plugin 3.0之前是能够将jar包中的res资源打包到apk中,从Android plugin 3.0之后就没法打包进apk。因为58APP中包含res的jar包还有很多,对于这种状况咱们的解决办法是自定义构建插件在构建过程当中将jar包中的res复制到生成目录。这次升级至3.3.1后自定义的plugin因为版本升级后一些task变化目前已失效,所以从新编写插件代码来实现此功能,主要代码以下:

C7689E79-DE69-49E2-B269-075C8FFC27E1.png

遗留问题

目前项目同步时会出现如下警告(虽然不影响编译,可是看起来仍是不舒服):

A740D31B-0831-421E-9F87-65788348F1BC.png

前两个警告,提示咱们getJavaCompiler会在2019年末删除,可是getJavaCompileProvider的返回值与getJavaCompiler不一致,目前暂没有特别好的办法,后续有好的办法再解决。

最后一个警告是提示咱们将compile替换为implementation或者api,可是咱们项目中都已替换,警告排查是咱们android-aspectjx插件语法致使,后续android-aspectjx升级后便可解决。

升级先后速度对比

通过测试升级后编译速度提高了大约33%,测试方式为clean后的全源码编译,下面是编译数据:

ADE37DFA-60CD-4386-9186-5AF27859650E.png

相关文章
相关标签/搜索