Android Studio项目Java 11环境下配置bootstrapClasspath无法访问framework.jar隐藏API的问题求助
嘿,我来帮你捋清楚这个问题的根源,再给出针对性的解决方案:
问题根源分析
这个问题主要和两个核心变化直接相关:
Java版本的模块系统差异
Java 9引入了JPMS(Java平台模块系统),Java 11是完全支持该系统的稳定版本。在Java 8及以前,JVM会直接加载bootstrapClasspath中的所有类,不管API是否被标记为隐藏;但Java 11+会严格遵循模块系统的规则,只有模块中显式导出的包才能被外部代码访问,而framework.jar里像TypedArray.getThemeAttributeId这类隐藏API所在的包(比如android.content.res)默认是未导出的,所以哪怕你把framework.jar加入了bootstrapClasspath,也无法正常访问。Kotlin插件的适配逻辑变化
Kotlin 2.2.0插件对Java 11+的模块系统做了完全严格的适配,100%遵循JPMS的访问规则;而2.0.0版本还没有强制执行这些规则,相当于“绕过”了模块系统的限制,所以旧版本能正常访问隐藏API,新版本就触发了合规检查导致失败。
解决方案
方案一:给Java编译任务添加模块导出参数
修改你build.gradle.kts中的JavaCompile任务配置,显式允许访问目标API所在的未导出包:
gradle.projectsEvaluated { tasks.withType<JavaCompile> { val newFiles = mutableListOf<File>().apply { add(File("libs/framework.jar")) options.bootstrapClasspath?.let { addAll(it.files) } } options.bootstrapClasspath = files(newFiles) // 根据你要访问的API所在包调整,这里以android.content.res为例 options.compilerArgs.add("--add-exports=android.content.res/android.content.res=ALL-UNNAMED") // 如果需要访问多个包的隐藏API,添加多个--add-exports参数,比如: // options.compilerArgs.add("--add-exports=android.view/android.view=ALL-UNNAMED") } }
方案二:同步配置Kotlin编译任务
因为你的项目用了Kotlin,只配置JavaCompile是不够的,还需要给Kotlin编译任务添加相同的参数:
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> { kotlinOptions { jvmTarget = "11" // 对应Java编译的--add-exports参数,包路径要和上面保持一致 freeCompilerArgs += "--add-exports=android.content.res/android.content.res=ALL-UNNAMED" } }
方案三:全局JVM参数配置(谨慎使用)
如果上面的配置还是不生效,可以尝试在gradle.properties中添加全局JVM参数,强制允许访问目标包:
org.gradle.jvmargs=--add-exports=android.content.res/android.content.res=ALL-UNNAMED
注意这个是全局配置,可能会影响项目中的其他模块,所以优先用前两个方案。
补充说明
为什么Java 8和旧Kotlin插件能正常工作?
- Java 8没有模块系统,JVM不会检查包的导出状态,只要
framework.jar在bootstrapClasspath里,就能直接访问里面的所有API,包括隐藏的。 - Kotlin 2.0.0插件对Java 11的模块系统适配不够严格,没有强制执行JPMS的访问规则,所以即使没有显式声明导出,也能“偷偷”访问隐藏API;而2.2.0插件完全遵循了模块系统的规范,所以必须显式声明要访问的未导出包。
内容来源于stack exchange

