1

我有两个应用程序:一个名为my-app.apk,另一个名为 my -service.apk。服务应用只定义了一个Android Service,它可以被主应用绑定以执行一些方法。这是使用 Android的 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. 完全没想到的是,它只会以不同的数据作为参数调用不同的方法。虽然我可以从底层的角度理解。也许他们这样做是为了确保这个界面是高性能的......

所以这是我的问题:

  1. 最好的升级策略是什么?根本不删除/更改旧方法和顺序?
  2. 我注意到当my-service.apk升级(重新安装)时,该服务会丢失my-app.apk。通常,系统会重新安排服务,这通常会在崩溃时执行。如何确保my-app.apk再次获取服务?注意新安装的软件包?

预先感谢您的帮助!:-)

干杯,马克

4

1 回答 1

4

如果你看一下 AIDL 编译器生成的代码,你会看到 RPC via 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,

然后调用方使用该编号将调用的方法映射到平面 Parcel 缓冲区,并由 IPC 的接收方选择生成的方法来解组序列化参数数据,最后调用真正的实现。

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

于 2017-01-29T11:21:43.610 回答