AGP 7.4.2 AAR打包问题及升级AGP 8.4.0解决方案
问题背景
在Androd项目开发中遇到了AGP 7.4.2版本的AAR打包问题,以及升级到AGP 8.4.0后R.id字段变化导致的编译问题。
问题1:AGP 7.4.2 AAR打包失败
问题现象
项目使用AGP 7.4.2时出现奇怪的现象:
- 直接引入module:编译和运行完全正常
- 打包成AAR后引入:出现D8编译器错误
错误信息:
bash
> Task :library:mergeDebugJavaResource FAILED
Execution failed for task ':library:mergeDebugJavaResource'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkAction
> com.android.tools.r8.CompilationFailedException: Compilation failed to complete
> com.android.tools.r8.utils.AbortException: Error: java.lang.NullPointerException
at YourClass$1.class (Anonymous inner class)
问题分析
这是AGP 7.4.2中D8编译器的一个已知Bug,具体表现为:
- 在AAR打包过程中,D8编译器处理匿名内部类时出现NullPointerException
- 直接module引用时不会触发此bug,只有在AAR打包时才会出现
- 错误指向
YourClass$1.class
等匿名内部类文件
尝试的解决方案
方案1:修改匿名内部类
将匿名内部类改为命名内部类:
java
// 修改前
Timer timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// 执行任务
}
}, delay);
// 修改后
private static class CustomTimerTask extends TimerTask {
@Override
public void run() {
// 执行任务
}
}
Timer timer = new Timer();
timer.schedule(new CustomTimerTask(), delay);
结果:失败,问题依然存在
方案2:移除Stream API
移除Java 8 Stream API的使用:
java
// 修改前
import java.util.stream.Collectors;
List<DataItem> result = dataList.stream()
.filter(item -> item.isValid())
.collect(Collectors.toList());
// 修改后
List<DataItem> result = new ArrayList<>();
for (DataItem item : dataList) {
if (item.isValid()) {
result.add(item);
}
}
结果:失败,问题依然存在
方案3:降级AGP版本
尝试降级到AGP 7.3.1:
gradle
classpath 'com.android.tools.build:gradle:7.3.1'
结果:失败,问题依然存在
方案4:调整gradle配置
尝试各种gradle.properties配置:
properties
android.enableD8.desugaring=true
android.enableR8=false
android.enableR8.fullMode=false
结果:失败,问题依然存在
最终解决方案:升级到AGP 8.4.0
经过各种尝试都无法解决问题后,最终选择升级到AGP 8.4.0:
gradle
// 项目级 build.gradle
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:8.4.0'
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.8.20'
}
}
properties
# gradle/wrapper/gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
配套更新:
gradle
// 模块级 build.gradle
android {
namespace 'com.example.yourpackage' // AGP 8.0+强制要求
compileSdkVersion 34
defaultConfig {
targetSdkVersion 34
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
结果:成功解决D8编译器Bug,AAR打包正常
问题2:AGP 8.0+ R.id字段变化导致的switch语句问题
问题现象
升级到AGP 8.4.0后,出现新的编译错误:
bash
> Task :app:compileDebugJavaWithJavac FAILED
MainActivity.java:56: 错误: 需要常量表达式
case R.id.button1:
^
问题原因
AGP 8.0+改变了R.id字段的生成方式:
AGP 7.x生成的R.java:
java
public final class R {
public static final class id {
public static final int button1 = 0x7f050001; // 编译时常量
public static final int button2 = 0x7f050002; // 编译时常量
}
}
AGP 8.0+生成的R.java:
java
public final class R {
public static final class id {
public static int button1; // 运行时赋值,不是常量
public static int button2; // 运行时赋值,不是常量
}
}
由于Java要求switch语句的case标签必须是编译时常量,而AGP 8.0+的R.id字段不再是编译时常量,因此switch语句无法编译通过。
解决方案:switch改为if-else
需要将所有使用R.id的switch语句改为if-else结构:
修改前:
java
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.button1:
doAction1();
break;
case R.id.button2:
doAction2();
break;
}
}
修改后:
java
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.button1) {
doAction1();
} else if (id == R.id.button2) {
doAction2();
}
}
复杂switch语句的处理
对于复杂的switch语句,如DetailActivity.java
:
修改前:
java
switch (v.getId()) {
case R.id.btn_back:
finish();
break;
case R.id.btn_play:
if (isPlaying) {
mediaPlayer.pause();
} else {
startPlayback();
}
break;
case R.id.btn_settings:
// 处理设置相关逻辑
break;
// ... 更多case
}
修改后:
java
int id = v.getId();
if (id == R.id.btn_back) {
finish();
} else if (id == R.id.btn_play) {
if (isPlaying) {
mediaPlayer.pause();
} else {
startPlayback();
}
} else if (id == R.id.btn_settings) {
// 处理设置相关逻辑
}
// ... 更多else if
注意:内部使用常量的switch语句可以保留,如:
java
// 这种switch可以保留,因为使用的是常量
switch (dataItem.getType()) {
case Constants.TYPE_HEADER:
// 处理逻辑
break;
case Constants.TYPE_CONTENT:
// 处理逻辑
break;
}
总结
问题解决路径
-
AGP 7.4.2 AAR打包问题
- 现象:module正常,AAR打包失败
- 原因:D8编译器Bug
- 解决:升级到AGP 8.4.0
-
AGP 8.0+ R.id问题
- 现象:switch语句编译失败
- 原因:R.id不再是编译时常量
- 解决:switch改为if-else
关键配置
最终的项目配置:
gradle
// build.gradle
classpath 'com.android.tools.build:gradle:8.4.0'
// gradle-wrapper.properties
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
// 模块build.gradle
android {
namespace 'com.example.yourpackage'
compileSdkVersion 34
targetSdkVersion 34
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
通过升级AGP版本和修改switch语句,最终彻底解决了所有编译问题。