AIDL 接口中间插入方法导致第三方调用异常
问题现象
IServiceAidlInterface 新增 unInit 接口后,未更新 AIDL 的第三方 App 出现:
start()调用正常registerListener()调用异常
根本原因
AIDL Transaction Code 机制
AIDL 基于 Android Binder IPC,客户端调用服务端方法时,传递的不是方法名,而是一个整数编号 transaction code。Android 编译器按 .aidl 文件中方法的声明顺序从 1 开始依次分配该编号。
两端(客户端 Proxy / 服务端 Stub)各自根据自己持有的 .aidl 文件生成代码,调用链如下:
客户端 Proxy 服务端 Stub
registerListener() → 发送 code=N → onTransact(code=N) → 路由到第 N 个方法
只要两端 code 映射一致,通信正常。一旦在接口中间插入新方法,后续所有方法的 code 全部错位,两端映射不再匹配。
本次变更的错位分析
unInit 被插入在 stop 和 registerListener 之间:
| Transaction Code | 服务端(新 AIDL) | 第三方客户端(旧 AIDL) | 结果 |
|---|---|---|---|
| 1 | start() |
start() |
✅ 正常 |
| 2 | stop() |
stop() |
✅ 正常 |
| 3 | unInit() |
registerListener() |
❌ 异常 |
| 4 | registerListener() |
unregisterListener() |
❌ 异常 |
| 5 | unregisterListener() |
(不存在) | ❌ 异常 |
- 第三方调用
registerListener()时发送 code = 3 - 服务端将 code 3 路由到
unInit() - 参数类型不匹配,调用异常
start()仍是 code 1,两端一致,所以不受影响
修复方案
新增 AIDL 方法必须追加到接口末尾,严禁插入到已有方法之间。
interface IServiceAidlInterface {
// ── 原有方法,顺序和位置不变 ──────────────────────────────
int start();
int stop();
void registerListener(in IServiceListener listener);
void unregisterListener(in IServiceListener listener);
// ── 新增方法统一追加到末尾 ────────────────────────────────
int unInit();
}
这样旧客户端持有的所有 transaction code 映射保持不变,向后兼容。
规范约束
| 操作 | 是否允许 |
|---|---|
| 在接口末尾追加新方法 | ✅ 允许 |
| 在已有方法之间插入新方法 | ❌ 禁止 |
| 删除或重排已有方法 | ❌ 禁止 |
| 修改已有方法的参数类型或顺序 | ❌ 禁止 |
AIDL 接口一旦对外发布,已有方法的顺序、签名、位置均不可更改,只能在末尾追加扩展。这与 Protobuf 字段编号、COM 接口 vtable 布局遵循同样的原则。