如何在Android中正确升级AIDL接口? [英] How to properly upgrade AIDL interfaces in Android?

查看:620
本文介绍了如何在Android中正确升级AIDL接口?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有两个应用程序:一个叫做 my-app.apk ,另一个叫 my-service.apk 。该服务应用仅定义了一个 Android服务,该绑定可由主要应用执行一些方法。这是使用Android的 AIDL 界面完成的,并且到目前为止效果很好。 / p>

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

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

出于好奇,我更改了 my的IRemote.aidl -app.apk 更改为以下内容(请注意区别!):

  package com.example.myservice; 
接口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 再次获得服务?请注意新安装的软件包吗?

在此先感谢您的帮助! :-)



干杯,
Marc

解决方案

如果看一下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,

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



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


I have two apps: One is called my-app.apk, the other my-service.apk. The service app just defines a single Android Service, which can be bound by the primary app to execute some methods. This is done using Androids AIDL interface, and it works great - so far.

Now I want to change the interface of the service, and I am wondering what I have to watch out for. I changed the file IRemote.aidl of my-service.apk to the following:

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

And just out of curiosity I changed the IRemote.aidl of my-app.apk to the following (note the differences!):

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

Now I got a completely unexpected result: The call to

printYeahThisHasADifferentNameAndParam("hello world");

from my application resulted in the log output "11". Why??

  1. I would not have expected a SecurityException at the bindService() call, although this would be adequate in a situation with completely different interfaces.
  2. What I would have expected would have been a RemoteException when executing the call, telling me that the method is not available.
  3. What I didn't expect entirely was that it will just call a different method with different data as parameters. Although I can understand it from a low-level point of view. Maybe they did it to ensure this interface is performant...

So here are my questions:

  1. What is the best upgrade-strategy? Simply do not delete/alter old methods and order?
  2. I noticed when my-service.apk is upgraded (re-installed) the service gets lost for my-app.apk. Normally the service gets re-scheduled by the system, which it typically does on crashes. How do I ensure my-app.apk aquires the service again? Watch out for newly installed packages?

Thank you in advance for your help! :-)

Cheers, Marc

解决方案

If you take a look at the code generated by the AIDL compiler, you will see that RPC via Binder calls methods by sequential number. Every method in the interface gets number assigned, like:

       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,

This number is then used by the calling side to map called method to flat Parcel buffer and by receiving side of IPC to select the generated method to unmarshall the serialized parameters data and finally call the real implementation.

Thus if you replace the method number 1 definition on the calling side, but still have old implementation on the receiving side, you will call the old implementation with completely bogus data. There is no type information in the serialized Parcel data of method arguments (besides the method number itself), so it will happily deserialize new method call parameters buffer as old ones and try to call implementation.

这篇关于如何在Android中正确升级AIDL接口?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆