修 ServiceConnection 回调在新版安卓上引发的闪退。崩溃长这样:
java.lang.NoSuchMethodError: public default void android.content.ServiceConnection
.onServiceConnected(android.content.ComponentName, android.os.IBinder, android.app.IBinderSession)
at bitter.jnibridge.JNIBridge.invoke(Native Method)
at bitter.jnibridge.JNIBridge$a.invoke(...)
at java.lang.reflect.Proxy.invoke(Proxy.java:1009)
at android.app.LoadedApk$ServiceDispatcher.doConnected(LoadedApk.java:2453)
新版安卓给 ServiceConnection 加了带第三个参数 IBinderSession 的回调,而且是 default 方法——正常写的应用什么都不用改就能兼容。
问题只出在用动态代理转发回调的框架上。它们按「方法名 + 签名」去查自己的实现表,把包括 default 实现在内的方法都往桥接层丢,而这个三参数方法在表里没有,于是抛 NoSuchMethodError。回调跑在主线程,一崩就是整个应用退出,通常发生在绑定登录、支付、广告这类后台服务时。
最典型的受害者是 Unity 5.x 的 bitter.jnibridge。但凡走这套机制的旧 SDK 都可能中招,不限于 Unity。
碰到三参数版本就不往下走了,直接改派两参数版本:
onServiceConnected(ComponentName, IBinder, IBinderSession) ← 系统给的新方法
↓
onServiceConnected(ComponentName, IBinder) ← 桥接层认识的老签名
这和平台给该 default 方法的默认实现完全一致——它本来就是转调两参数版的。桥接层拿到认得的签名,回调正常送达,异常根本不会发生。
只挂一个方法。进程里找不到对应的桥接类就说明用不上,模块直接退出。
模块有两套实现,做的事一模一样,只是用的 Xposed API 不同:
| 目录 | Release 文件名 | 用的 API | 适用框架 |
|---|---|---|---|
legacy/ |
ServiceConnectionFix-legacy-1-1.0.apk |
传统 Xposed API(de.robv.android.xposed) |
所有支持传统 Xposed API 的框架 |
xposed102/ |
ServiceConnectionFix-xposed102-1-1.0.apk |
libxposed API 102 | 实现了 libxposed API 102 的框架 |
两者包名相同,只能装一个。框架的 libxposed 实现不到 102 就下 legacy,功能完全一样。装完在框架模块列表里能看到、能勾作用域,就是认了;看不到就换另一个。
- 装 APK
- LSPosed / Vector 里启用模块,作用域勾上目标应用
- 强制停止那个应用再打开
推荐作用域里预置了一个已知会崩的应用,装上就能直接勾。其他中招的应用手动加进去就行。
Android 16+。这个崩溃只在 16 及以上出现,更低版本装不上。
需要 LSPosed / Vector 一类 Xposed 框架,以及能注入目标进程的 Zygisk 实现。
NeoZygisk 和 Vector 是同一位作者的产物,直觉上配套用应该最稳,通常也确实如此。但有个例外:
NeoZygisk 从 v2.2 起,在 64 位设备上不再注入 32 位应用(release notes 原话是 "no longer injects into 32-bit-only applications running on 64-bit devices",理由是"现代 32 位应用已经很少")。
于是当目标应用是纯 32 位的(APK 里只有 armeabi-v7a,没有 arm64-v8a),而设备是 64 位时,那个进程从来没被注入过,模块再怎么装都不会生效。
坑的地方在于它是静默失败:框架照样列出模块、照样让你勾作用域、全都显示正常,UI 上没有任何提示,就是没效果。
解法是换一个还管 32 位 zygote 的实现——Zygisk Next(Dr-TSNG/ZygiskNext)、ReZygisk 或 Meow Zygisk 都行。换之前先卸载旧的 Zygisk 模块并重启一次,避免模块 ID 冲突。