如何正确升级Android中的AIDL接口?

时间:2013-05-29 14:45:55

标签: java android android-service rpc android-binder

我有两个应用程序:一个名为 my-app.apk ,另一个名为 my-service.apk 。服务应用程序只定义了一个Android Service,它可以被主应用程序绑定以执行某些方法。这是使用Androids AIDL界面完成的,并且效果很好 - 到目前为止。

现在我想改变服务的界面,我想知道我需要注意什么。我将 my-service.apk 的文件IRemote.aidl更改为以下内容:

package com.example.myservice;
interface IRemote {
  void printHello();
  void print(int i);
}

出于好奇,我将 my-app.apk 的IRemote.aidl更改为以下内容(注意差异!):

package com.example.myservice;
interface IRemote {
  void printHello();
  void printYeahThisHasADifferentNameAndParam(String s);
}

现在我得到了一个完全出乎意料的结果:对

的调用
printYeahThisHasADifferentNameAndParam("hello world");
从我的应用程序

导致日志输出“11”。为什么?

  1. 我不希望在bindService()调用中遇到 SecurityException ,尽管这在接口完全不同的情况下是足够的。
  2. 执行调用时,我所期望的是 RemoteException ,告诉我该方法不可用。
  3. 完全没想到的原因是它只会使用不同的数据作为参数调用不同的方法。虽然我可以从低级别的角度理解它。也许他们这样做是为了确保这个界面具有良好的性能......
  4. 所以这是我的问题:

    1. 什么是最佳升级策略?只是不删除/改变旧方法并订购?
    2. 我注意到 my-service.apk 升级(重新安装)后, my-app.apk 的服务丢失了。通常,系统会重新调度服务,这通常会在崩溃时进行。如何确保 my-app.apk 再次获取服务?注意新安装的软件包?
    3. 提前感谢您的帮助! : - )

      干杯, 马克

1 个答案:

答案 0 :(得分:2)

如果您查看AIDL编译器生成的代码,您将看到RPC通过Binder按顺序号调用方法。接口中的每个方法都会分配编号,例如:

       SIZE = ::android::IBinder::FIRST_CALL_TRANSACTION + 0,
       SETSIZE = ::android::IBinder::FIRST_CALL_TRANSACTION + 1,
       READ = ::android::IBinder::FIRST_CALL_TRANSACTION + 2,
       WRITE = ::android::IBinder::FIRST_CALL_TRANSACTION + 3,
       SYNC = ::android::IBinder::FIRST_CALL_TRANSACTION + 4,

然后,调用方使用此数字将被调用的方法映射到平面包裹缓冲区,并通过接收IPC的一侧来选择生成的方法来解组序列化的参数数据,最后调用实际的实现。

因此,如果您在调用端替换方法编号1定义,但在接收端仍然具有旧实现,则将使用完全伪造的数据调用旧实现。方法参数的序列化Parcel数据中没有类型信息(除了方法编号本身),因此它很乐意将新方法调用参数缓冲区反序列化为旧的,并尝试调用实现。