前言
如果这篇文章可以解决你的问题,那么请无视本文章
本文章适用于使用JDK 1.8以上的构建环境
验证环境
Windows 11
Android Studio 2024.2.2
JAVA 17
AGP 8.8.0
Gradle 8.10.2
结论先行
Build.gradle添加如下编译参数(updateCompilerArgs)即可:
groovy
plugins {
id("com.android.application")
}
android {
...
gradle.projectsEvaluated {
tasks.named("compileDebugJavaWithJavac") {
//此处需要显式声明javac的task依赖resource编译完成
dependsOn("processDebugResources")
//如果只依赖framework.jar,只需要添加这一行
classpath = updateClasspath(classpath)
//如果只依赖core-libart.jar,只需要添加这一行
updateCompilerArgs(options.compilerArgs)
}
tasks.named("compileReleaseJavaWithJavac") {
//同上,release与debug分别声明对应的task依赖
dependsOn("processReleaseResources")
//如果只依赖framework.jar,只需要添加这一行
classpath = updateClasspath(classpath)
//如果只依赖core-libart.jar,只需要添加这一行
updateCompilerArgs(options.compilerArgs)
}
}
}
// 新增一个函数,以便多次调用
def updateCompilerArgs(List<String> compilerArgs) {
compilerArgs.add("--patch-module")
//路径需要根据实际情况调整
compilerArgs.add("java.base=${project.rootDir}/libs/core-libart.jar")
}
// 新增一个函数,以便多次调用
def updateClasspath(FileCollection classpathSet) {
List<File> newFileList = new ArrayList<>()
//路径需要根据实际情况调整
newFileList.addAll(Arrays.asList(
"${project.rootDir}/libs/core-libart.jar",
"${project.rootDir}/libs/framework.jar",
))
File sdkFile = null
for (File f : classpathSet.getFiles()) {
//将android.jar放到-classpath参数末尾即可
if ("android.jar" == f.getName()) {
sdkFile = f
} else {
newFileList.add(f);
}
}
if (sdkFile != null) {
newFileList.add(sdkFile)
}
return files(newFileList.toArray())
}
dependencies {
...
//compileOnly还是需要添加
compileOnly files('../libs/framework.jar')
compileOnly files('../libs/core-libart.jar')
}
完整经历
新建一个Android Project,移除androidx等不必要的依赖,确保是个最精简的Android Project(因为后面要排查编译参数,依赖过多会导致命令行特别长,不利于分析)
然后我使用下面这几行代码来代表几种情况:
- 引用一个Android SDK中的android.jar里不存在,但core-libart.jar中包含的类,例如
dalvik.system.VMRuntime; - 引用一个Android SDK中的android.jar存在的类,但是其中某个不存在或者hide注解修饰的方法,例如
DexFile.getDexFileOptimizationInfo()
java
package com.gwm.demo4androidwjava;
import android.app.Activity;
import android.os.Build;
import android.os.Bundle;
import dalvik.system.DexFile;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
try {
DexFile dexFile = new DexFile("test");
// 访问一个android.jar中不存在的类
String instructionSet = dalvik.system.VMRuntime.getInstructionSet(Build.CPU_ABI);
// 访问一个android.jar中存在的类,但是其方法不存在或者为hide
DexFile.OptimizationInfo optInfo = dexFile.getDexFileOptimizationInfo(
"test", instructionSet);
} catch (Exception e) {
e.printStackTrace();
}
}
}
编译,毫无疑问两处都会报错:
bash
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:17: error: cannot find symbol
String instructionSet = dalvik.system.VMRuntime.getInstructionSet(Build.CPU_ABI);
^
symbol: class VMRuntime
location: package dalvik.system
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:18: error: cannot find symbol
DexFile.OptimizationInfo optInfo = dexFile.getDexFileOptimizationInfo(
^
symbol: method getDexFileOptimizationInfo(String,String)
location: variable dexFile of type DexFile
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:
uses or overrides a deprecated API.
Recompile with -Xlint:deprecation for details.
从AOSP中编译一个core-libart.jar,并拷贝到该Android Project下app模块内的libs目录中:

然后按照这篇文章中添加对framework.jar的依赖的方式,修改build.gradle:
groovy
plugins {
alias(libs.plugins.android.application)
}
android {
...
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
gradle.projectsEvaluated {
tasks.getByName("compileDebugJavaWithJavac") {
//经过验证发现,修改classpath会导致编译task列表顺序紊乱,从而报错提示找不到R.class相关内容;
//因此此处需要显式声明javac的task依赖resource编译完成
dependsOn("processDebugResources")
classpath = reorderClasspath(classpath.getFiles())
}
tasks.getByName("compileReleaseJavaWithJavac") {
//同上,release与debug分别声明对应的task依赖
dependsOn("processReleaseResources")
classpath = reorderClasspath(classpath.getFiles())
}
}
}
// 新增一个函数,以便多次调用
def reorderClasspath(Set<File> classpathSet) {
List<File> newFileList = new ArrayList<>()
newFileList.addAll(Arrays.asList(
"./libs/core-libart.jar",
))
File sdkFile = null
for (File f : classpathSet) {
//将android.jar放到-classpath参数末尾即可
if ("android.jar" == f.getName()) {
sdkFile = f
} else {
newFileList.add(f);
}
}
if (sdkFile != null) {
newFileList.add(sdkFile)
}
return files(
newFileList.toArray()
)
}
dependencies {
...
compileOnly files('libs/core-libart.jar')
}
编译,依然报错,报错信息一模一样:
bash
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:17: error: cannot find symbol
String instructionSet = dalvik.system.VMRuntime.getInstructionSet(Build.CPU_ABI);
^
symbol: class VMRuntime
location: package dalvik.system
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:18: error: cannot find symbol
DexFile.OptimizationInfo optInfo = dexFile.getDexFileOptimizationInfo(
^
symbol: method getDexFileOptimizationInfo(String,String)
location: variable dexFile of type DexFile
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:
uses or overrides a deprecated API.
Recompile with -Xlint:deprecation for details.
由于失败阶段依然为compileDebugJavaWithJavac,而新版本Android Studio(或许是新版本Gradle导致?)无法使用--debug参数重新run一遍,于是使用命令行调试这个任务:
bash
.\gradlew.bat app:compileDebugJavaWithJavac --debug
查找关键字Compiler arguments,得到这段数据(格式化后展现):
bash
Compiler arguments: \
-source 11 \
-target 11 \
-d \
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes \
-encoding UTF-8 \
-g \
-sourcepath "" \
-proc:none \
-s C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\generated\ap_generated_sources\debug\out \
-XDuseUnsharedTable=true \
-classpath \
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\libs\core-libart.jar;D:\dev_tools\android-sdk\build-tools\35.0.0\core-lambda-stubs.jar;\
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\compile_and_runtime_not_namespaced_r_class_jar\debug\processDebugResources\R.jar;\
D:\dev_tools\android-sdk\platforms\android-36\android.jar \
-XDstringConcat=inline \
--system C:\Users\HP\.gradle\caches\8.10.2\transforms\57f8b045b26807d762a56357fc05473c\transformed\output\jdkImage \
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java
可以看到,classpath中core-libart.ja已经在android.jar前面了,按照framework.jar的导入经验,这样应该是可以生效的(事实也确实如此,如果此处是用framework.jar的API,导入的是framework.jar,到这一步应该已经编译通过了)
于是将上述参数转换成javac命令行,并添加-verbose显示详细编译信息:
bash
javac -source 11 -target 11 -d C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes -encoding UTF-8 -g -sourcepath "" -proc:none -s C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\generated\ap_generated_sources\debug\out -XDuseUnsharedTable=true -classpath "C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\libs\core-libart.jar;D:\dev_tools\android-sdk\build-tools\35.0.0\core-lambda-stubs.jar;C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\compile_and_runtime_not_namespaced_r_class_jar\debug\processDebugResources\R.jar;D:\dev_tools\android-sdk\platforms\android-36\android.jar" -XDstringConcat=inline --system C:\Users\HP\.gradle\caches\8.10.2\transforms\57f8b045b26807d762a56357fc05473c\transformed\output\jdkImage C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java -verbose
三个修改:
-classpath参数后面需要添加引号;- 命令行前加
javac - 命令行末尾加
-verbose
由于这是个最小化的Android Project,整个编译命令输出并不多:
bash
[parsing started SimpleFileObject[C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java]]
[parsing completed 15ms]
[loading /modules/java.base/module-info.class]
[search path for source files: ]
[search path for class files: D:\dev_tools\jdk-21.0.4\lib\modules,C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\libs\core-libart.jar,D:\dev_tools\android-sdk\build-tools\35.0.0\core-lambda-stubs.jar,C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\compile_and_runtime_not_namespaced_r_class_jar\debug\processDebugResources\R.jar,D:\dev_tools\android-sdk\platforms\android-36\android.jar]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/app/Activity.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/os/Build.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/os/Bundle.class)]
# DexFile.class并不是从android.jar中加载的
[loading /modules/java.base/dalvik/system/DexFile.class]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/ComponentCallbacks2.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/ComponentCallbacks.class)]
[loading /modules/java.base/java/lang/Object.class]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/KeyEvent.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/KeyEvent$Callback.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/os/Parcelable.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/InputEvent.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/LayoutInflater.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/LayoutInflater$Factory2.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/LayoutInflater$Factory.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/View.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/View$OnCreateContextMenuListener.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/accessibility/AccessibilityEventSource.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/graphics/drawable/Drawable.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/graphics/drawable/Drawable$Callback.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/Window.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/Window$Callback.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/ContextThemeWrapper.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/ContextWrapper.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/Context.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/ViewDebug.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/ViewDebug$ExportedProperty.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/ViewDebug$IntToString.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/view/ViewDebug$FlagToString.class)]
[loading /modules/java.base/java/lang/Override.class]
[loading /modules/java.base/java/lang/annotation/Annotation.class]
[loading /modules/java.base/java/lang/annotation/Retention.class]
[loading /modules/java.base/java/lang/annotation/RetentionPolicy.class]
[loading /modules/java.base/java/lang/annotation/Target.class]
[loading /modules/java.base/java/lang/annotation/ElementType.class]
[checking com.gwm.demo4androidwjava.MainActivity]
[loading /modules/java.base/java/io/Serializable.class]
[loading /modules/java.base/java/util/List.class]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/IntentSender.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/IntentSender$SendIntentException.class)]
[loading /modules/java.base/java/lang/Error.class]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/util/AndroidException.class)]
[loading /modules/java.base/java/lang/Exception.class]
[loading /modules/java.base/java/lang/Throwable.class]
[loading /modules/java.base/java/lang/RuntimeException.class]
[loading /modules/java.base/java/io/IOException.class]
[loading /modules/java.base/java/io/FileNotFoundException.class]
[loading /modules/java.base/java/lang/Class.class]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/pm/PackageManager.class)]
[loading D:\dev_tools\android-sdk\platforms\android-36\android.jar(/android/content/pm/PackageManager$NameNotFoundException.class)]
[loading /modules/java.base/java/lang/AutoCloseable.class]
[loading C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\compile_and_runtime_not_namespaced_r_class_jar\debug\processDebugResources\R.jar(/com/gwm/demo4androidwjava/R.class)]
[loading C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\compile_and_runtime_not_namespaced_r_class_jar\debug\processDebugResources\R.jar(/com/gwm/demo4androidwjava/R$layout.class)]
[loading /modules/java.base/java/lang/String.class]
[loading /modules/java.base/java/io/File.class]
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:17: error: cannot find symbol
String instructionSet = dalvik.system.VMRuntime.getInstructionSet(Build.CPU_ABI);
^
symbol: class VMRuntime
location: package dalvik.system
# DexFile$OptimizationInfo.class也并不是从android.jar中加载的
[loading /modules/java.base/dalvik/system/DexFile$OptimizationInfo.class]
C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java:18: error: cannot find symbol
DexFile.OptimizationInfo optInfo = dexFile.getDexFileOptimizationInfo(
^
symbol: method getDexFileOptimizationInfo(String,String)
location: variable dexFile of type DexFile
[total 419ms]
Note: C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors
可以看出,在编译DexFile跟DexFile.OptimizationInfo时,javac并没有去android.jar中加载class,而是加载的/modules/java.base/
因此,看起来,编译的时候,javac除了classpath的定义外,还有一部分依赖是在此之外的规则在作用;
排查下整个编译的命令行参数,可以确定是``--system```这一参数所致:
bash
--system C:\Users\HP\.gradle\caches\8.10.2\transforms\57f8b045b26807d762a56357fc05473c\transformed\output\jdkImage
该目录下有一个release文件,其内容正是定义了名为java.base的module:
bash
JAVA_VERSION="21"
MODULES="java.base"
根据官方文档来看,这是一个JAVA9以后引入的一个模块化编译的机制,--system用于加载一个全局的编译环境,而这里面就包括JAVA基础库的支持;正因如此,当我们想指定使用自己的core-libart.jar中的API时,会由于优先级问题,无法找到;
既然现在的难题是JAVA9以后的模块化编译机制引入的,那么解决也需要依赖这个机制;
原理很简单,我们通过--patch-module参数来给这个java.base模块"打补丁",把我们core-libart.jar打到java.base中去,即可解决问题;
快速验证,在之前的javac命令行末尾添加--patch-module参数:
bash
javac -source 11 -target 11 -d C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\javac\debug\compileDebugJavaWithJavac\classes -encoding UTF-8 -g -sourcepath "" -proc:none -s C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\generated\ap_generated_sources\debug\out -XDuseUnsharedTable=true -classpath "C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\libs\core-libart.jar;D:\dev_tools\android-sdk\build-tools\35.0.0\core-lambda-stubs.jar;C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\build\intermediates\compile_and_runtime_not_namespaced_r_class_jar\debug\processDebugResources\R.jar;D:\dev_tools\android-sdk\platforms\android-36\android.jar" -XDstringConcat=inline --system C:\Users\HP\.gradle\caches\8.10.2\transforms\57f8b045b26807d762a56357fc05473c\transformed\output\jdkImage C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\src\main\java\com\gwm\demo4androidwjava\MainActivity.java -verbose --patch-module java.base=C:\Users\HP\AndroidStudioProjects\Demo4AndroidWJAVA\app\libs\core-libart.jar
编译通过。
接下来考虑如何把这个参数通过build.gradle传递出去即可:
groovy
gradle.projectsEvaluated {
tasks.getByName("compileDebugJavaWithJavac") {
...
options.compilerArgs.add("--patch-module")
options.compilerArgs.add("java.base=${project.rootDir}/app/libs/core-libart.jar")
}
tasks.getByName("compileReleaseJavaWithJavac") {
...
classpath = reorderClasspath(classpath.getFiles())
options.compilerArgs.add("--patch-module")
options.compilerArgs.add("java.base=${project.rootDir}/app/libs/core-libart.jar")
}
编译同样通过,使用.\gradlew.bat app:compileDebugJavaWithJavac --debug可以看到参数也是添加成功了的;