App 适配 Android15( API 35),修复 Gradle 8.0 和 ButterKnife 不兼容问题

现在 AI 时代,我觉得很多技术博客没必要写了,但最近将一个七年前的 Android 项目兼容 Android15,花了不少时间,累麻了,而且一些问题 AI 也没帮到我,所以做个记录,希望帮到有需要的人。

Google Play 政策要求(2025 年 8 月 31 日前必须使用目标 API ≥ 35),App 更新必须以 Android 15(API 级别 35),否则不能发布 App。之前发布的以后不能更新,只能下载。国内这些应用市场还没这个要求,可能将来也会有。当然,国内市场也有很多屁事,比如近期小作文,要发布鸿蒙版本,否则不给通过软著,而没软著就不能发布市场。。。

扯远了,总之,如果你也有这个需求,可以接着看。

这是我目前的环境

Android Studio Iguana 2023
JDK 17
AGP 8.3.2

我之前使用使用 targetSdkVersion 33,AGP 是 7.x,项目打包运行都没问题,在这个基础上修改兼容 35。

建议不要尝试在 AGP 7.x 情况下编译 targetSdkVersion 35,我也不想升级 Gradle 版本,但我试过两天都没搞定,报错如下

android-35 java.lang.NullPointerException

网上有帖子貌似可以突破解决这问题,在 gradle.properties 配置如下:

android.aapt2FromMavenOverride=/Users/guozh/Library/Android/sdk/build-tools/35.0.0/aapt2

反正上面方案对我没用,总是会报 D8 错误,然后我又尝试了各种禁用方案,也尝试在混淆文件中排除,反正都试了,都没解决,没辙,只能升级到 AGP 8.x。

AGPBI: {“kind”:”error”,”text”:”java.lang.NullPointerException”,”sources”:[{“file”:”/Users/xxx/Library/Android/sdk/platforms/android-35/android.jar”}],”tool”:”D8″}

这是我目前使用的 AGP 版本

classpath ‘com.android.tools.build:gradle:8.3.2’
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip

修改 targetSdkVersion 和 compileSdkVersion 到 35,先不要同步,把以下内容一起改了。

工程下所有模块都要使用 namespace,不止 app,其他模块都添加,namespace 的值是 AndroidManifest.xml 的包名 package。

android {
    namespace ‘com.guozh.xxx’
     …

     defaultConfig {

     }
}

然后在每个模块下添加 buildConfig = true,一样,每个模块的 gradle 都要加,这是 AGP8.0 的新特性。

 buildFeatures {
   // aidl true // 如果模块没用AIDL,这里不用管,可注释
   buildConfig = true
 }

再处理根目录 build.gradle,将 repositories 迁移到 settings.gradle,这也是新特性,不改也是可以,但我总感觉有几个编译报错与这里有关,索性一起改了。

根目录 build.gradle

buildscript {
    repositories {
        google()
        mavenCentral()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:8.3.2'
    }
}

根目录 settings.gradle

pluginManagement {
    repositories {
        google()
        mavenCentral()
        gradlePluginPortal() // 这是 Gradle 官方插件仓库
    }
}

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        // 其他远程仓库,根据你原项目添加和修改
        // maven { url 'https://jitpack.io' }
        // maven { url 'https://maven.aliyun.com/repository/google' } // 兼容旧版 google
        // maven { url 'https://maven.aliyun.com/repository/jcenter' } // 兼容旧版 jcenter
        // 集中声明 flatDir,让所有模块都能访问 app/libs
        flatDir {
            dirs 'app/libs'
        }
    }
}

rootProject.name=‘xxxx'
include ':app'
include ‘:xxxx'

出现元素值必须为常量表达式,这是因为 R 文件字段不再是常量,Gradle8.0+ 和 ButterKnife 的不兼容导致,这个问题我也是在官方issues找到解决方案的。现在推荐 viewBinding ,但这是之前老项目,替换成本太高了,只能想办法突破。

修改 app/build.gradle (或者其他使用 ButterKnife 的模块的 build.gradle),在 app 模块的 build.gradle 文件中,在文件的最底部,添加以下代码块:

tasks.withType(JavaCompile).configureEach {
    options.fork = true
    options.forkOptions.jvmArgs += [
        '--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED',
        '--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED',
    ]
}

修改 gradle.properties (根目录)

# 解决 @BindView 报错 "元素值必须为常量表达式"
# 这个属性告诉 AGP 不要使用非 final 的资源 ID
android.nonFinalResIds=false

顺便把 gradle.properties 里的 JDK 版本也固定了,这一步做不做无所谓,因为 JDK 的版本一般在 Android Studio 的设置了。如果没有,在 Gradle JDK 选项中,从下拉菜单里选择一个 版本为 17 或更高 的 JDK。通常 Android Studio 会自带一个,名字可能类似于 jbr-17 或 Android Studio java home version 17,选择它即可。

Build, Execution, Deployment -> Build Tools -> Gradle

如果存在多模块引用资源,比如在 app 引用其他模块 basic_res 的颜色、文本,也会出现元素值必须为常量表达式的报错。因为新版 AGP 默认将 R 文件的字段设置为了非 final 。解决这个问题很简单,要么拷贝一份资源到 app 模块,要么使用用完整的包名来引用。

比如修改前是这样的

ResUtils.getColor(context, R.color.main);

修改后

ResUtils.getColor(context, com.guozh.basic.res.R.color.main);

ok,希望以上能帮到你。

本文由老郭种树原创,转载请注明:https://guozh.net/app-compatible-with-android-15/

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注