Loading...
墨滴

掀乱书页的风

2021/06/16  阅读:177  主题:默认主题

Android Gradle升级的血泪史(一)

背景

  1. 随着项目越来越大,编译时间成为了我们的瓶颈,升级Gradle是不二法门,另外新版本的android-gradle插件也提供了一些新的特性来帮助我们提升开发效率
  2. 之前的Gradle编译插件已落后两年左右,很多新功能特性无法使用,甚至有部分SDK无法升级,升级Gradle编译插件到较新稳定版本提升构建速度,提升开发效率。

调研

升级之前项目的状态

工具 版本号
android-gradle 3.3.2
gradle 4.10.1
compileSdkVersion 28
buildToolsVersion 28.0.3

升级之后

工具 版本号
android-gradle 4.0.2
gradle 6.1.1
compileSdkVersion 29
buildToolsVersion 29.0.2

最新版本android-gradle是4.2.0,为什么不升到最新版呢,因为58app中引入的第三方插件,有一些没有适配最新的AGP版本,比如qigsaw,导致我们没法升级到最新版,所以我们选择升级到4.0.2版本

qigsaw支持AGP4.1
qigsaw支持AGP4.1

Android Gradle 插件版本更新说明(3.4.0-4.2.0)

https://developer.android.com/studio/releases/gradle-plugin

SDK升级统计

SDK 当前版本 升级后版本 升级方式
qigsaw 1.8.3 1.9.1 手动适配
AndResGuard 1.2.32 1.2.33 手动适配
WBRouter 1.2.10 1.2.21 手动适配
Wafers 1.8.1 1.8.12 手动适配
packager 3.3.0 3.3.1 手动适配
walle 0.0.16 1.0.1 手动适配
WBRetrofit 3.0.0 3.1.1 手动适配
greendao 3.2.2 3.3.0 官方支持
bugly:symtabfileuploader 2.1.0 2.2.1 官方支持
bytex 0.2.2 0.2.5 官方支持
aspecj 2.0.4 2.0.10 官方支持
butterknife 10.0.0 10.2.1 官方支持
kotlin 1.3.50 1.4.30 官方支持

AGP升级到4.0对开发者的影响

这里只列出了一些对我们影响比较大的功能和特性,详细的更新说明可以查看官方文档

R8 默认处于启用状态

R8 将脱糖、压缩、混淆、优化和 dex 处理整合到了一个步骤中,从而显著提升了构建性能。 R8 是在 Android Gradle 插件 3.3.0 中引入的,对于使用插件 3.4.0 及更高版本的应用和 Android 库项目,R8 现已默认处于启用状态

增量注解处理

如果在 gradle.properties 文件中设置了 android.databinding.incremental=true,数据绑定注解处理器支持增量注解处理。这项优化提高了增量构建的性能。

注解处理器支持增量构建部分列表
注解处理器支持增量构建部分列表

此外,KAPT 1.3.30 及更高版本也支持增量注释处理器,可以通过在 gradle.properties 文件中添加 kapt.incremental.apt=true 来启用此支持。

视图绑定(ViewBinding)

kotlin-android-extensions已经被Google标记为过时插件了,建议替换为viewBinding的方式,开启方式 在每个模块的 build.gradle 文件中添加以下代码

  android {
      viewBinding.enabled = true
  }

支持 Maven Publish 插件

Android Gradle 插件支持 Maven Publish Gradle 插件,可让您将构建工件(apk、aab文件)发布到 Apache Maven 代码库。Android Gradle 插件会为应用或库模块中的每个构建变体工件创建一个组件,可以使用它来自定义要发布到 Maven 代码库的发布内容。

新的默认打包工具

在构建应用的Debug版本时,会使用一个新的打包工具 zipflinger 来构建 APK。这一新工具应该能够提高构建速度。如果新的打包工具无法正常运行。可以通过在 gradle.properties 文件中添加以下代码来恢复使用旧的打包工具:

  //google只提供了这个配置,是有bug的
  android.useNewApkCreator=false
  //必须额外添加这个配置
  android.useNewJarCreator=true

默认 NDK 版本

Android Gradle 插件会选择一个默认版本来编译源代码文件。如果没有下载默认版本就会编译报错,以前选择的是最新下载的 NDK 版本。现在必须使用 android.ndkVersion 属性替换插件选择的默认版本,否则会编译报错。

缺少清单类

升级之前,如果在清单文件中定义自定义权限,Android Gradle 插件通常会生成 Manifest.java 类,用于以字符串常量的形式添加自定义权限。

在AGP升级之后无法生成清单类。无法使用Manifest.java获取自定义权限。怎么来解决呢:

  • 通过完全限定名称引用自定义权限。例如,"com.example.myapp.permission.DEADLY_ACTIVITY"。
  • 定义自己的常量,如下所示:
  public final class CustomPermissions {
  public static final class permission {
    public static final String DEADLY_ACTIVITY="com.example.myapp.permission.DEADLY_ACTIVITY";
  }

D8 和 R8 中的 Java 8 库脱糖

Android Gradle 插件现在支持使用多种 Java 8 语言 API,而无需为应用设置最低 API 级别。

通过一个称为“脱糖”的过程,Android Studio 3.0 及更高版本中的 DEX 编译器 D8 已经为 Java 8 语言功能(如 lambda 表达式、默认接口方法、try-with-resources 等等)提供了大量的支持。在 Android Studio 4.0 中,脱糖引擎经过扩展,能够使 Java 语言 API 脱糖。现在可以在支持旧版 Android 的应用中添加过去仅在最新 Android 版本中可用的标准语言 API(如 java.util.streams)。

此版本支持下面一组 API:

  • 顺序流 (java.util.stream)
  • java.time 的子集
  • java.util.function
  • java.util.{Map,Collection,Comparator} 的最近新增内容
  • 可选内容(java.util.Optional、java.util.OptionalInt 和 java.util.OptionalDouble)以及对上述 API 很有用的一些其他新类
  • java.util.concurrent.atomic 的一些新增内容(AtomicInteger、AtomicLong 和 AtomicReference 的新方法)
  • ConcurrentHashMap(包含 Android 5.0 的问题修复)

Feature-on-feature依赖

在以前的 Android Gradle 插件版本中,AAB模式下,所有功能模块都只能依赖于应用的基本模块。使用 Android Gradle 插件 4.0.0 时,您现在可以添加依赖于其他功能模块的功能模块。也就是说,:video 功能可以依赖于 :camera 功能,而后者依赖于基本模块,如下图所示

:video 功能模块依赖于 :camera 功能,而后者依赖于 :app 基本模块。
:video 功能模块依赖于 :camera 功能,而后者依赖于 :app 基本模块。

当app请求下载某个功能模块时,它也会下载该模块所依赖的其他功能模块。可以在模块的 build.gradle 文件中声明功能对功能的依赖性。例如,:video 模块声明对 :camera 的依赖性,如下所示:

  // In the build.gradle file of the ':video' module.
dependencies {
    // All feature modules must declare a dependency
    // on the base module.
    implementation project(':app')
    // Declares that this module also depends on the 'camera'
    // feature module.
    implementation project(':camera')
    ...
}

升级遇到的问题及解决方案

  1. gradle4.10.1 -> 6.1.1 语法变更 variant.variantData.variantConfiguration 过时,改为 variant.variantData.variantConfiguration。
    解决方案:手动修改适配资源路径混淆插件 AndResGuard

  2. ndkversion 如果不指定会使用插件默认的版本,如果本地或者 beta 平台没有对应的 ndk 就会编译报错
    解决方案:强制指定 ndk 版本
    android { // gradle 升级需要 ndkVersion "21.1.6352462" }
    联系 teg 同学将 beta 打包 ndk 从 r20 升级到 r21

    本地开发时必须下载 r21 对应的 ndk,否则编译报错

  3. beta 编译遇到错误

    [exec] FAILURE: Build failed with an exception. [exec] [exec] * What went wrong: [exec] Execution failed for task ':58WuxianClient:mergeWubaReleaseClasses'. [exec] > A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade [exec] > Too many entries (99851) [exec] [exec] * Try: [exec] Run with --stacktrace option to get the stack trace. Run with --debug option to get more log output. Run with --scan to get full insights. [exec]

    原因

    • 在构建应用的 Debug 版本时,androd-gradle 会使用一个新的打包工具 zipflinger 来构建 APK。这一新工具用来提高构建速度
    • beta 平台打包是使用基于 qigsaw 的打包方式,而 zipflinger 与 qigsaw 插件冲突

    解决方案:gradle.properties 同时添加
    android.useNewApkCreator=false
    android.useNewJarCreator=false (qigsaw 官方反馈的方案)

  4. wafers 模式下报错
    原因:AGP 3.6 修改了 R 文件的生成方式,不在 transform 的 dirInput 中生成 R.class
    解决方案:使用 R.jar 中的 R.class, 并且修改获取时机

  5. R.txt 文件编译位置发生变化,导致 beta 构建失败
    解决方案:修改打包脚本,将 58WuxianClient/build/intermediates/symbols/ {local.buildFlavor}Release/R.txt"

  6. symtabfileuploader 插件报错

    Could not find method leftShift() for arguments [com.tencent.bugly.gradle.BuglyPlugin$_createUploadTask_closure12@74764337] on task ':58WuxianClient:uploadWubaDebugSymtabFile' of type org.gradle.api.DefaultTask.

    解决方案:从 2.1.0 版本升级到 classpath 'com.tencent.bugly:symtabfileuploader:2.2.1'

    [更新日志](https://bugly.qq.com/docs/release-notes/release-bugly-plugin/?v=20200622202242)

  7. FrescoSoloader 找不到
    原因:gradle 对依赖校验更加严格,导致无法引用 Fresco 内部的 com.facebook.soloader:soloader
    解决方案:手动添加 soloader 依赖

  8. qigsaw 插件报错
    原因:qigsaw 插件是我们从官方 fork 下来,为了适用我们的业务进行了修改,并没有与官方保持同步
    解决方案:qigsaw 插件升级支持 AGP4.0,把官方适配代码合并到我们的分支, 升级到 1.8.4

  9. 版本依赖冲突: [com.google.protobuf:protobuf-java:3.4.0->3.10.0]
    原因:IDE 自己的 lib 冲突

    +--- org.jetbrains.trove4j:trove4j:20160824 | --- com.android.tools.build:aapt2-proto:0.4.0 | --- com.google.protobuf:protobuf-java:3.4.0 -> 3.10.0

    +--- com.android.tools.analytics-library:tracker:27.0.2 | | +--- com.android.tools:annotations:27.0.2 | | +--- com.android.tools:common:27.0.2 () | | +--- com.android.tools.analytics-library:protos:27.0.2 () | | +--- com.android.tools.analytics-library:shared:27.0.2 (*) | | +--- com.google.protobuf:protobuf-java:3.10.0

    解决方案:强制指定使用 3.10.0

  10. compileSdkVersion=29 api 变更

    • RecentTaskInfo.topActivity 由非空变为 nullable --- kotlin 调用修改
    • kotlin 校验更加严格,java 文件中用@Nullable 声明的可空变量或方法,kotlin 中使用,由黄色警告变为强制报错
    • get/set 方法, 有的 api 里面语法糖只能代表 get 方法
    • Type inference failed. Please try to specify type arguments explicitly. getParcelable ... 改动比较多,主要是 kotlin 修改
  11. greendao 报错

    Caused by: java.lang.NoSuchMethodError: org.gradle.api.tasks.TaskInputs.property(Ljava/lang/String;Ljava/lang/Object;)Lorg/gradle/api/tasks/TaskInputs; at org.greenrobot.greendao.gradle.Greendao3GradlePlugin.createGreendaoTask(Greendao3GradlePlugin.kt:60) at org.greenrobot.greendao.gradle.Greendao3GradlePlugin.access$createGreendaoTask(Greendao3GradlePlugin.kt:14)

    at org.greenrobot.greendao.gradle.Greendao3GradlePlugin 1.execute(Greendao3GradlePlugin.kt:47)

    原因:低版本没有适配 AGP4.0 解决方案:升级 greendao3.2.2 => 3.3.0

    https://greenrobot.org/greendao/changelog/

  12. The option 'android.useDeprecatedNdk' is deprecated.
    从 gradle.properties 中删掉

  13. libraries 中找不到 BuildConfig.APPLICATION_ID

    原因:Android Studio 3.5 之后做出了变更 BuildConfig: Deprecate APPLICATION_ID in libraries. It is at best misleading, so it is marked as deprecated and replaced by LIBRARY_PACKAGE_NAME.
    解决方案:使用 library 中 LIBRARY_PACKAGE_NAME

  14. 全源码编译时 qigsaw 插件相关的 aar 有 3 个下不下来
    原因:implement 语法更加严格,传递性依赖无法引用 解决方案:qigsaw 源码中 依赖改为 api 依赖

  15. HouseLib 下引用 com.wuba.video.R 找不到
    原因:implementation 依赖会无法找到
    解决方案:引用 houseLib 的 R 文件,历史逻辑,不知道为何要使用 com.wuba.video.R

  16. Debug 模式下,minifyenable=false,会导致 app 中 dex 为空
    原因:wbrouter 中为了兼容 bundle 打包(dynamic-feature),Tranform 返回了 SCOPE_FULL_WITH_FEATURES 的 scope,,这个在 3.6 以下没有问题(其实正常来讲这样是没有问题的),但 AGP3.6 应该是做了特殊的判断触发了 dexBuilder 在 wbrouter 之前执行,导致执行结果异常
    解决方案:升级了 wbrouter

  17. bytex 升级, 10.8 版本引入了 bytex
    原因:Debug 模式下,minifyenable=false,会导致 app 中 dex 为空, 与 16 相同 解决方案:从 0.2.2 升级到 0.2.5

  18. aspecj 构建失败

    Execution failed for task ':58WuxianClient:transformClassesWithAjxForWubaRelease'. [exec] > Cannot cast object 'com.android.build.gradle.internal.pipeline. TransformTask 1@6800653f' with class 'com.android.build.gradle.internal.pipeline. TransformTask 1' to class 'com.android.build.gradle.internal.pipeline.TransformTask'

    解决方案:
    aspectjx 升级 2.0.4 -> 2.0.10
    aspectjrt 升级 1.8.13 -> 1.9.5

    https://github.com/HujiangTechnology/gradle_plugin_android_aspectjx

  19. AspectJTestEnabled=true 开启后 NoClassDefFoundError

    java.lang.NoClassDefFoundError: Failed resolution of: Lcom/wuba/WubaSetting; at com.wuba.application.AppDataResolver.gf(SourceFile:44) at com.wuba.application.AppDataResolver.init(SourceFile:23) at com.wuba.application.WubaHybridApplication.attachBaseContext(SourceFile:42)

    解决方案:exclude 'com.wuba.WubaSetting'

  20. wafers 模式下 feature 库 build 文件中 deps.txt 找不到
    原因:deps.txt 目录发生变化
    解决方案:feature_transitive_deps 修改为新目录 packaged_dependencies

  21. WBRouter 扫描不到 anjuke 下面的某些 module 代码
    解决方案:手动注册 LogisticsCenter.register("com.wuba.wbrouter.routes.WBRouter

    AJKCommonBusiness") ····

  22. Attempt to use @BindView for an already bound ID 0

    • 原因:ViewBinding 与 ButterKnife 特性冲突
    • 解决方案:Butterknife: 10.0.0-->10.2.1

    AGP 3.6 or newer requires version 10.2.1 because the R.txt changed to use 0 for every resource entry value. In 10.2.1 we now generate our own unique values.link

  23. java.io.IOException: Cannot run program "/data/soft/jdk/jdk1.8.0_66/jre/bin/java": error=7, 参数列表过长
    原因:kotlin 的 bug:https://github.com/JetBrains/kotlin/commit/ce2ae58ffb1e9a4e54c4b4aa8ec29298a1113be1
    解决方案:kotlin 版本升级 1.3.50-->1.4.30

  24. 混淆打包后 apk 中文件丢失,导致部分 RN 页面加载不出来
    原因:android-gradle 高版本与 Proguard 产生的 bug 解决方案:开启 R8 编译后,问题解决

  25. aar 发布失败
    原因:gradle 版本升级后,api 兼容问题
    解决方案:重新适配 aar 发布脚本

  26. MSA sdk 会弹出 toast 提示,采用 hooktoast 拦截

  27. 混淆文件中配置 dontshrink 后,没有生产 usage.txt 文件,没法查看 R8 移除了哪些类
    原因:dontshrink 配置后,不会压缩代码,所以 usage.txt 不生成是合理的

  28. 个人中心商家版,切换 RN 页面异常,混淆字段缩减导致,将内部类 keep 处理

  29. 剪包缺少 kotlin 库
    原因:gradle 升级触发了 javassist 的 bug
    解决方案:对异常代码块 try catch

  30. beta 打包 oom
    原因:需求开源码太多,导致服务器扛不住
    解决方案:加大 beta 编译内存

  31. 招聘急招页面崩溃
    原因:wbretrofit 混淆问题
    解决方案:wbretrofit 发布新版本,支持 R8 混淆

  32. 剪包无法跳转:R8 开启后只保留 log.e 其他的 log 会被移除,因为 R8 开启了混淆优化 原因:qigsaw 对 r8 的支持有 bug
    解决方案:对比 qigsaw 官方代码,将 58 的 qigsaw 进行修改支持 R8

  33. 招聘 so 崩溃问题 so 问题,开发方已经修复

  34. 兼职 wubadialog 无法弹出崩溃 混淆问题 keep 出来

  35. 全职以及其他场景 wubadialog 无法弹出崩溃 aar 问题,重新发布 aar

上面列出这么多,并不是给大家讲流水账的,而是为了说明在升级的过程中,真的是一步一个坑,两步一个雷走过来的,解决方案,看似简单,但是在定位的过程中非常痛苦

升级后主要收益

  1. 包大小减少了3.6M
  2. release编译速度提升50%左右,由原来的接近30分钟,缩短到15分钟左右

主要是因为开启了R8,那么R8如何对包大小和编译速度进行影响的呢

Android Gradle升级的血泪史(二)

掀乱书页的风

2021/06/16  阅读:177  主题:默认主题

作者介绍

掀乱书页的风