如何优雅地升级 AGP 到 8.0 版本

大半夜在这儿解决编译问题可真行。

最近稍微有时间来接着看看电表。以前提到过,电表算是我用来练手各种 Android 开发技术的试验田,因此从 IDE 到三方库的绝大部分依赖版本用得都相当激进。

以前升级依赖都还算平滑,按照 IDE 的提示一路点完就是了。但这次 Android Studio 升级到 Flamingo(2022.2.1)版本,连带 AGP 升级到 8.0 之后,问题就比较头疼了,开始出现了大把的编译问题。这里写一篇文章记录一下。

JDK 版本

默认版本提高

从 AGP 8.0 开始,Gradle 构建默认采用 JDK 17。记得去 IDE 的设置里把 Gradle JDK 版本改一下。

JDK 版本导致的编译问题

大多数时候我们会手动在 build.gradle 里指定 JVM 字节码的兼容性,比如说电表目前采用的是 Java 11:

compileOptions {
    sourceCompatibility JavaVersion.VERSION_11
    targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
    jvmTarget = '11'
}

这个选项在之前很好用,但根据 Stack Overflow 上这个问题的回答,由于 AGP 8.0 和 kapt 的兼容性问题, 最终会导致设置的 jvmTarget 只会应用到 Kotlin 编译上,kapt 任务不会采用这个而是使用默认值(对于 AGP 8.0 和 Flamingo,默认的 JDK 版本是 17)。这个时候编译就会出现报错:

Execution failed for task ':app:kaptGenerateStubsDebugKotlin'.
> 'compileDebugJavaWithJavac' task (current target is 11) and 'kaptGenerateStubsDebugKotlin' task (current target is 17) jvm target compatibility should be set to the same Java version.
  Consider using JVM toolchain: https://kotl.in/gradle/jvm/toolchain

解决办法是手动指定一下 kapt 任务的 jvmTarget

tasks.withType(org.jetbrains.kotlin.gradle.tasks.KaptGenerateStubs).configureEach {
    kotlinOptions {
        jvmTarget = '11'
    }
}

R8

默认优化等级的提高

从 AGP 8.0 开始,R8 默认会开启 Full Mode,在这个模式下的优化会更加激进。因此原先的 ProGuard 规则有可能不太够用。最典型的一个例子就是 Retrofit。在升级 AGP 8.0 之后,会出现以下报错:

java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at retrofit2.v.b(SourceFile:363)
    at p.a1.c(SourceFile:31)
    at retrofit2.y0.invoke(SourceFile:45)
    at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
    at $Proxy8.c(Unknown Source)

反混淆之后是:

java.lang.ClassCastException: java.lang.Class cannot be cast to java.lang.reflect.ParameterizedType
    at retrofit2.HttpServiceMethod.retrofit2.HttpServiceMethod parseAnnotations(retrofit2.Retrofit,java.lang.reflect.Method,retrofit2.RequestFactory)(SourceFile:363)
       retrofit2.ServiceMethod retrofit2.ServiceMethod.parseAnnotations(retrofit2.Retrofit,java.lang.reflect.Method)
    at androidx.camera.camera2.internal.ZoomControl.retrofit2.ServiceMethod retrofit2.Retrofit.loadServiceMethod(java.lang.reflect.Method)(SourceFile:31)
    at retrofit2.Retrofit$1.java.lang.Object invoke(java.lang.Object,java.lang.reflect.Method,java.lang.Object[])(SourceFile:45)
    at java.lang.reflect.Proxy.invoke(Proxy.java:1006)
    at $Proxy8.c(Unknown Source)

上面的 issue 里提到需要增加一些新的 ProGuard 的规则。其实这个已经在去年就有了,不过 Retrofit 一直没有发版。所以目前只能手动在 Retrofit 里加入下列规则:

# Keep generic signature of Call, Response (R8 full mode strips signatures from non-kept items). 
-keep,allowobfuscation,allowshrinking interface retrofit2.Call 
-keep,allowobfuscation,allowshrinking class retrofit2.Response 
​
# With R8 full mode generic signatures are stripped for classes that are not 
# kept. Suspend functions are wrapped in continuations where the type argument 
# is used. 
-keep,allowobfuscation,allowshrinking class kotlin.coroutines.Continuation 

AGP 8.0 默认参数导致的编译失败

在老版本 AGP 中,当 R8 在混淆时对于找不到的类会发出警告。而 8.0 开始则提高到了错误级别(即强制开启了 android.r8.failOnMissingClasses 选项),会卡编译流程。不过这个问题也比较好解决,AGP 会自动生成一个文件来记录这些类,按照提示将这个文件里的内容追加到 ProGuard 的混淆规则里即可。如果用到了 Okhttp 的话会遇到这个问题,官方在 issue 里回复已经修在了 5.0.0 版本上(不过这是个测试版,感觉 backport 回 4.x 应该也不是什么难事?)。

最后

除了上面这些之外,还有一个最重要的就是彻底移除了 Transform,不过我还没写过字节码插件,不是特别清楚这个。其他一些破坏性改动可以看看 Google 的官方文档,写得还是比较清楚的。

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇