【问题标题】:How to properly upgrade AIDL interfaces in Android?Android中如何正确升级AIDL接口?
【发布时间】:2013-05-24 21:34:21
【问题描述】:

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

所以这是我的问题:

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

提前感谢您的帮助! :-)

干杯, 马克

【问题讨论】:

  • 1:你有多少个aidl文件?对不起,但我不明白。 2:你用的是什么IDE?无论如何,当您更改aidl 文件时,您可以简单地清理aidl 项目并重新编译服务和​​应用程序项目。如果您将服务推送到您的设备,它会在您绑定到它时启动(您必须使用 falg BIND_AUTO_CREATE)
  • 我只有一个AIDL文件,但当然必须将它复制到定义服务的项目中使用服务的项目中。由于代码最终在 Java 中自动生成,然后编译成 2 个 APK,因此您可能有两个应用程序 AB 的界面不同。根本不是IDE的问题,我已经在使用BIND_AUTO_CREATE了。
  • 好的,现在很清楚了。我建议你做一件事:创建一个只有 1 个 AIDL 文件的新项目。您在服务和应用项目中都引用了这个项目。所以你摆脱了所有文件的差异。无论如何,如果有一些 AIDL 问题,Android 不会引发异常,而是方法调用可能存在一些不匹配(不同的返回值,奇怪的行为,...)
  • 不过,如果我对此 AIDL 文件进行更改并且 B 升级,A 可能仍在使用旧界面 - 直到它也升级。所以最后有A,A*和B(A*是旧界面的应用程序)。所以只有一个文件不会改变我遇到的问题。让我们更清楚一点:用户同时安装了 my-app.apkmy-service.apk。现在他升级了my-service.apk,但他根本不碰my-app.apk(可能一个月后才升级)。
  • 是的,没错。当您更改 AIDL 时,您必须升级这两个应用程序。我不知道是否有办法解决这个“问题”。但我不认为这是一个问题。是的,如果 AIDL 不同,它应该引发异常,这是唯一可以以更好的方式管理的事情

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


【解决方案1】:

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

       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 数据中没有类型信息(除了方法号本身),因此它会很乐意将新的方法调用参数缓冲区反序列化为旧的并尝试调用实现。

【讨论】:

    猜你喜欢
    • 2019-04-17
    • 1970-01-01
    • 2022-06-13
    • 2021-07-13
    • 2019-06-08
    • 1970-01-01
    • 2022-07-13
    • 1970-01-01
    • 2013-11-10
    相关资源
    最近更新 更多