通过AIDL在服务之间传递活动对象

5

我正在尝试让几个不同包中的服务共享一个公共对象。每个服务都必须调用相同的对象。

例如,服务A(来自APK A)实例化了一个自定义对象,我希望服务B和C(来自APK B和C)检索此对象的引用并调用其某些方法。

我在Android参考文献中发现可以使用Parcel实现这一点:

活动对象

Parcel 的一个不寻常的特性是能够读取和写入活动对象。对于这些对象,实际对象的内容并没有被写入,而是写入一个特殊的标记引用该对象。从 Parcel 中读取对象时,您不会得到该对象的新实例,而是得到一个操作最初写入的完全相同对象的句柄。有两种类型的活动对象可用。

Binder 对象是 Android 通用跨进程通信系统的核心设施。IBinder 接口描述了一个带有 Binder 对象的抽象协议。任何此类接口都可以写入到 Parcel 中,在读取时,您将收到实现该接口的原始对象或一个特殊代理实现,该代理实现将调用返回到原始对象。要使用的方法包括 writeStrongBinder(IBinder)、writeStrongInterface(IInterface)、readStrongBinder()、writeBinderArray(IBinder[])、readBinderArray(IBinder[])、createBinderArray()、writeBinderList(List)、readBinderList(List)、createBinderArrayList()。

我尝试通过 AIDL 将我的对象(继承自 Binder)传递,但什么都不起作用,当我尝试从方法 createFromParcel(Parcel in) 中检索引用时,总是会出现 ClassCastException。

我的代码示例:
public class CustomObject extends Binder implements Parcelable {

  public CustomObject() {
    super();
  }

  public static final Parcelable.Creator<CustomObject> CREATOR = new Parcelable.Creator<CustomObject>() {
  public CustomObject createFromParcel(Parcel in) {
    IBinder i = in.readStrongBinder();
      // HOW TO RETRIEVE THE REFERENCE ??
      return null;
    }

    @Override
    public CustomObject[] newArray(int size) {
      return null;
    }
  };

  @Override
  public int describeContents() {
    return 0;
  }

  @Override
  public void writeToParcel(Parcel dest, int flags) {
    dest.writeStrongBinder(this);
  }
}

有人已经完成了吗?

提前致谢!


你曾经搞清楚过如何做这件事吗? 我几年前尝试过,但放弃了。现在我再次尝试,遇到了和你一样的问题/结果。 - edharned
很遗憾,为了在对象上调用方法,我改变了我的处理方式,每次都要检索对象的副本。虽然不完美,但它是有效的。但我仍然想知道是否有解决这个问题的方法。 - nbe_42
3个回答

2

这里有两种方法。

简单方法:使用aidl处理对象本身

  • It seems you have an existing AIDL interface through which you pass this 'custom object' as a parcel. Don't do that. Instead:
  • The object which you pass through should be itself described by AIDL. Say, for example, you call it ICustomObject.aidl.
  • In this case you do not need to make the object Parcelable. You probably don't even need to write the above code; just use one AIDL-described type in another. For example add a line like this to the main AIDL for service A:

    ICustomObject getCustomObject();
    
  • In service A, within the Stub class you've already got, you'll need to simply return something inheriting from ICustomObject.

  • In services B and C, you can simply call that method to get hold of an ICustomObject. Simple! No parcels, no readStrongBinder(), nothing.

更难

如果您按照以上方法操作,Android工具链将生成Java代码来进行对象的编组和解组。您也可以自己编写代码。

ICustomObject myObjectWhichActuallyLivesInAnotherProcess = ICustomObject.Stub.asInterface(parcel.readStrongBinder())

甚至

ICustomObject myObjectWhichActuallyLivesInAnotherProcess = (ICustomObject)parcel.readStrongBinder().queryLocalInterface("com.your.custom.object");

然而,如果你把所有东西都变成aidl,我认为你的生活会更加理智。

关于类共享的说明

你可能需要创建一个Android“库项目”,其中包含ICustomObject.aidl,以便在构建A、B和C的项目之间共享生成的类。


0

在我自己广泛研究之后,我认为这实际上是不可能的。你得到的ClassCastException是将一个BinderProxy(它是一个扩展IBinder的私有类)强制转换为你的实际类(CustomObject)的结果。当涉及跨其他进程引用Binders时,总是似乎传递了一个BinderProxy,并且这是他们在声明“原始实现该接口的对象或特殊代理实现”时所指的对象。 BinderProxy确实允许您调用IBinderonTransact()方法,但没有其他操作。

我真诚地认为文档在陈述“原始对象”被传递到其他进程时是不正确的,因为该类的文档还有几个明显抄袭和错误的实例(链接)


0

我不仅想分享数据。对此,简单的AIDL机制就足够了。 我需要分享一个复杂的对象,该对象必须是唯一的,并且具有所有服务都可以调用的一些方法。 - nbe_42

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接