整个包的Andr​​oid ResultReceiverAndr、oid、ResultReceiver

2023-09-04 11:47:51 作者:<置杯茶香早淡發>

我在程序包A的活性(SignerClient),和包B,在服务(为MyService)

I have an activity in package A (SignerClient), and a service in package B (MyService)

活动的resultreceiver:

The activity's resultreceiver:

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");

执行此操作时,客户端和服务器在同一个包工作正常。但在这种情况下,我得到:

Doing this when client and server are in the same package works fine. But in this case i get:

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)

我在想什么?是我的想法,甚至有可能吗?

What am I missing? Is my idea even possible?

推荐答案

是的,你的想法是可行的。该 ClassNotFoundException的异常被抛出,因为你试图unparcel那是在一个不同的过程由不同的的ClassLoader 创建的类。

Yes, your idea is possible. The ClassNotFoundException exception is thrown because you are trying to unparcel a class that was created in a different process by a different ClassLoader.

ResultReceiver 类实现 Parcelable 接口,适用于进程间调用(IPC),但是读你在您需要使用相同的类加载器的服务对象,这是用于客户端应用程序(即活动)创建对象。要获取服务端的类加载器,叫 createPackageContext 的方法来传递客户端软件包名称和的 CONTEXT_INCLUDE_ code | CONTEXT_IGNORE_SECURITY 标志的组合。这将返回从中可以得到正确的类加载器对象的客户端语境对象

ResultReceiver class implements Parcelable interface which is suitable for inter-process calls (IPC), however to read your object in the service you need to use the same ClassLoader, that was used for object creation in the client application (i.e. in the activity). To get that ClassLoader on the service side, call createPackageContext method passing client package name and CONTEXT_INCLUDE_CODE|CONTEXT_IGNORE_SECURITY combination of flags. This will return client Context object from which the correct ClassLoader object can be obtained.

例如:

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;
}

我只是用它在我的code - 就像一个魅力

I've just used it in my code - works like a charm.

更新 不过,我建议考虑使用使者,而不是 ResultReceiver 的。它实现了 Parcelable 接口,不需要进行这样的扩展类加载器的问题是不可能的。而且,这也是建议在官方文档。

UPDATE However I suggest to consider using Messenger instead of ResultReceiver. It implements Parcelable interface and does not need to be extended thus the ClassLoader issue isn't possible. And it's also recommended in the official documentation.

更新2 如果你还在preFER使用 ResultReceiver 看看罗伯特的回答在这个线程。它看起来比哈克情况下操作清晰,更简单。

UPDATE 2 In case you still prefer to use ResultReceiver take a look at Robert's answer in this thread. It looks cleaner and simpler than hacky context manipulations.