Android Billing 1.1 在 SHIELD 上出现 NoSuchMethodError
结论
问题的原因是代码压缩工具重命名了 IInAppBillingService 的方法,而 SHIELD 系统框架中存在同名类,两者因此发生冲突。
异常中的方法名为:
a(Landroid/os/IBinder;)Lcom/android/vending/billing/IInAppBillingService;
正常情况下,该方法应为:
IInAppBillingService.Stub.asInterface(IBinder)
需要在 ProGuard/R8 配置中保留 com.android.vending.billing 下相关类和方法的名称,再重新生成完整安装包:
-keep class com.android.vending.billing.** { *; }
如果条件允许,还应升级依赖旧版 Billing Library 的第三方库,停止使用 com.android.billingclient:billing:1.1。
原因
Billing Library 1.1 使用以下接口:
com.android.vending.billing.IInAppBillingService
部分 SHIELD 固件的 /system/framework/framework.jar 中也包含这个类。Android 通常优先加载系统框架中的类,因此运行时加载的可能不是 APK 中经过压缩处理的版本,而是设备固件提供的版本。
构建 release 包时,ProGuard 或 R8 可能会把 APK 中的 asInterface() 重命名为 a(),并同步修改调用位置:
IInAppBillingService.Stub.a(service)
SHIELD 系统框架中的 IInAppBillingService.Stub 没有经过应用自身的混淆,其中仍然只有 asInterface()。运行时找不到静态方法 a(IBinder),便会抛出:
java.lang.NoSuchMethodError
异常还明确指出,类声明来自:
/system/framework/framework.jar:classes2.dex
这是判断系统同名类覆盖问题的重要线索。
解决步骤
1. 添加 ProGuard/R8 保留规则
在应用模块实际使用的混淆配置文件中加入:
-keep class com.android.vending.billing.** { *; }
例如,在 app/proguard-rules.pro 中添加:
# SHIELD 固件中可能存在同名 IInAppBillingService。
# 保留类名和成员名,避免 asInterface() 被重命名。
-keep class com.android.vending.billing.** { *; }
同时确认 release 构建确实引用了该文件:
android {
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile(
'proguard-android-optimize.txt'
), 'proguard-rules.pro'
}
}
}
使用 Kotlin DSL 时可以写成:
android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
如果 Billing 依赖封装在自行维护的 Android Library 中,也可以将规则放入该库的 consumer rules:
android {
defaultConfig {
consumerProguardFiles 'consumer-rules.pro'
}
}
在 consumer-rules.pro 中加入:
-keep class com.android.vending.billing.** { *; }
2. 清理并重新构建安装包
保留规则只会对重新压缩、混淆的产物生效。添加规则后,需要重新生成 release 包,并在设备上安装新产物。
可以检查 mapping.txt,确认以下方法没有再次被重命名为 a:
com.android.vending.billing.IInAppBillingService$Stub.asInterface
3. 升级 Billing 依赖
com.android.billingclient:billing:1.1 已经非常陈旧。应优先升级直接依赖它的第三方库,或者在兼容性允许的情况下,统一项目中的 Billing Library 版本。
升级前需要核对:
- 第三方库是否硬编码了 Billing 1.1 的 API。
- 项目中是否同时引入了多个 Billing Library 版本。
- 新版本的购买确认、恢复购买和订阅处理流程是否需要调整。
- 目标商店和 SHIELD 固件是否支持所选版本。
不能仅通过 Gradle 强制覆盖版本,就认定第三方库与新版本兼容。如果第三方库调用了旧版专有 API,强制升级可能引发新的二进制兼容问题。
为什么不应捕获这个异常
在 onServiceConnected() 外层捕获 NoSuchMethodError,只能避免进程立即崩溃,却无法获得有效的 IInAppBillingService:
mService = IInAppBillingService.Stub.asInterface(service);
这一步失败后,后续调用也无法正常执行:
mService.isBillingSupported(...)
因此,需要解决方法名不一致的问题,而不是忽略异常。
注意事项
- 该问题通常只会出现在开启
minifyEnabled的构建中。debug 包运行正常,并不能证明 release 包没有问题。 -dontwarn只能隐藏构建警告,不能阻止方法重命名,也无法解决NoSuchMethodError。- 不建议通过关闭整个应用的代码压缩来规避问题。针对
com.android.vending.billing.**添加保留规则即可。 - 如果添加规则后,异常中的缺失方法仍然是
a(...),说明当前构建类型没有加载该规则,或者崩溃设备上安装的仍是旧包。 - 如果异常变成了其他 Billing API 或 Binder 错误,则需要重新分析。该保留规则只解决系统同名类与混淆方法名不一致的问题。
备注:内容仅供参考。
