【问题标题】:Android ResultReceiver across packages跨包的 Android ResultReceiver
【发布时间】:2011-08-10 06:03:01
【问题描述】:

我在包 A (SignerClient) 中有一个活动,在包 B (MyService) 中有一个服务

活动的结果接收者:

private ResultReceiver resultreceiver = new ResultReceiver(null) {
            @Override
            protected void onReceiveResult(int resultCode, Bundle resultData) {
            ...
            }
        };

启动服务:

Intent intent = new Intent("com.example.STARTSERVICE");
intent.putExtra("resultreceiver", resultreceiver);            
startService(intent);

接收端:

 ResultReceiver rr = (ResultReceiver) intent.getParcelableExtra("resultreceiver");

当客户端和服务器在同一个包中时这样做可以正常工作。但在这种情况下,我得到:

FATAL EXCEPTION: IntentService[MyService]
android.os.BadParcelableException: ClassNotFoundException when unmarshalling: com.example.cryptoclient.SignerClient$1
at android.os.Parcel.readParcelable(Parcel.java:1883)
at android.os.Parcel.readValue(Parcel.java:1771)
at android.os.Parcel.readMapInternal(Parcel.java:2008)
at android.os.Bundle.unparcel(Bundle.java:208)
at android.os.Bundle.getParcelable(Bundle.java:1100)
at android.content.Intent.getParcelableExtra(Intent.java:3396)
at org.axades.service.MyService.onHandleIntent(MyService.java:28)
at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:59)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:123)
at android.os.HandlerThread.run(HandlerThread.java:60)

我错过了什么?我的想法是否可行?

【问题讨论】:

  • 因为这个话题很老了:支持库中的这个类被添加到包android.os从android Lollipop开始,这个问题似乎在这个过程中解决了。确保导入正确的...(见下面的答案)

标签: android ipc android-service


【解决方案1】:

我想跨包使用ResultReceiver,而不得不加载另一个包的上下文对我来说似乎不合适......毕竟,接收包不需要知道@987654322 的特定子类@used,它只需要能够调用send() 并让IPC binder 魔术发生。需要提供特定的子类似乎是一个设计缺陷。

有一个解决方法:

public static ResultReceiver receiverForSending(ResultReceiver actualReceiver) {
    Parcel parcel = Parcel.obtain();
    actualReceiver.writeToParcel(parcel,0);
    parcel.setDataPosition(0);
    ResultReceiver receiverForSending = ResultReceiver.CREATOR.createFromParcel(parcel);
    parcel.recycle();
    return receiverForSending;
}

此代码将您的ResultReceiver 的某个子类的实例转换为ResultReceiver 本身的实例,但仍将结果发送到您原来的actualReceiverreceiverForSending 可以通过 Intent 额外发送到另一个包,并且可以很好地解组。

【讨论】:

  • 谢谢!很好的实施!事实上,当我们可以使用它时,加载一些包的上下文是个坏主意。
  • 我什至没有使用 IPC,我仍然不得不使用这种方法。很好的解决方案……但很不幸。
  • 在我的 IPC 场景中完美运行 :)
  • 绝对精彩!
  • 这似乎更适用于 android.os.ResultReceiver 实现,这样不会违反 RestrictedApi
【解决方案2】:

是的,你的想法是可能的。抛出ClassNotFoundException 异常是因为您试图解包由不同ClassLoader 在不同进程中创建的类。

ResultReceiver 类实现适用于进程间调用 (IPC) 的 Parcelable 接口,但是要在服务中读取您的对象,您需要使用相同的 ClassLoader,该 ClassLoader 用于在客户端应用程序中创建对象(即在活动中)。要在服务端获取该 ClassLoader,请调用 createPackageContext 方法传递客户端包名称和 CONTEXT_INCLUDE_CODE|CONTEXT_IGNORE_SECURITY 标志组合。这将返回客户端 Context 对象,从中可以获取正确的 ClassLoader 对象。

例子:

public int onStartCommand(Intent intent, int flags, int startId) {
  try {

// assuming SignerClient activity is located in the package "com.example.client.A"
    Context context = createPackageContext("com.example.client.A", Context.CONTEXT_INCLUDE_CODE | Context.CONTEXT_IGNORE_SECURITY);
    ClassLoader cl = context.getClassLoader();

    Bundle bundle = intent.getExtras();
    bundle.setClassLoader(cl);
    ResultReceiver rr = bundle.getParcelable("resultreceiver");

//... your interaction with ResultReceiver ...
    rr.send(1, null);   // will result in a onReceiveResult call in the client activity

  } catch (NameNotFoundException e) {
    Log.e("MyService", "SignerClient package context was not found", e);
    throw new RuntimeException(e);
  }
  return START_STICKY;
}

我刚刚在我的代码中使用了它 - 就像一个魅力。

更新
不过我建议考虑使用Messenger 而不是ResultReceiver。它实现了Parcelable 接口并且不需要扩展因此ClassLoader 问题是不可能的。也是recommended in the official documentation

更新 2
如果您仍然喜欢使用ResultReceiver,请查看Robert's answer in this thread。它看起来比 hacky 的上下文操作更干净、更简单。

【讨论】:

  • 我认为你错了,根据developer.android.com/reference/android/os/…看来你仍然需要使用类加载器。
  • @MiroKropacek 不是真的。在 Messenger 的情况下,您只需要设置一个类加载器来实例化通过 Messenger 从一个进程传递到另一个进程的对象(即,如果您在 Message.object 字段中向进程 A 发送进程 B 中定义的类的实例)。 Messenger 本身不需要设置类加载器,可以安全地用于传递由系统类加载器加载的类型的原语和对象,如 String、Point 等。
  • 文档推荐 IntentService 而不是 MessengerService (developer.android.com/reference/android/app/IntentService.html),这对于不需要持续连接的一次性请求很有意义。但是,他们没有说明如何在同一个应用程序中的特殊情况之外实际发送结果是没有意义的。
【解决方案3】:

出于某种原因,我只在android.support.v4.os.ResultReceiver 中遇到了这个错误。将导入更改为android.os.ResultReceiver 使ipc 结果立即生效。无需解决方法!

【讨论】:

猜你喜欢
  • 2011-05-29
  • 1970-01-01
  • 2016-02-26
  • 1970-01-01
  • 2017-06-28
  • 1970-01-01
  • 1970-01-01
  • 2011-07-26
  • 1970-01-01
相关资源
最近更新 更多