跨包的Android ResultReceiver

end*_*dre 12 android ipc android-service

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

活动的结果接收者:

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

启动服务:

Intent intent = new Intent("com.example.STARTSERVICE");
intent.putExtra("resultreceiver", resultreceiver);            
startService(intent);
Run Code Online (Sandbox Code Playgroud)

接收结束:

 ResultReceiver rr = (ResultReceiver) intent.getParcelableExtra("resultreceiver");
Run Code Online (Sandbox Code Playgroud)

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

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)
Run Code Online (Sandbox Code Playgroud)

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

RTS*_*lio 24

我想使用ResultReceiver跨包并且必须加载其他包的上下文对我来说似乎不对...毕竟,接收包不需要知道所ResultReceiver使用的特定子类,它只需要能够打电话send()让IPC绑定魔术发生.需要使用特定的子类似乎是一个设计缺陷.

有一个解决方法:

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;
}
Run Code Online (Sandbox Code Playgroud)

此代码将您的某个子类的ResultReceiver实例转换为ResultReceiver自身的实例,但仍然会将结果发送到您的原始实例actualReceiver.该receiverForSending可在发送Intent额外的另一种包装,可以解组就好了.


Ido*_*lon 16

是的,你的想法是可能的.的ClassNotFoundException,因为你正试图unparcel这是在不同的工艺由不同的创建类异常被抛出ClassLoader.

ResultReceiverclass实现Parcelable适用于进程间调用(IPC)的接口,但要在服务中读取您需要使用相同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;
}
Run Code Online (Sandbox Code Playgroud)

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

更新
然而我建议考虑使用Messenger而不是ResultReceiver.它实现了Parcelable接口,不需要扩展,因此无法实现ClassLoader问题.它也在官方文档中推荐.

更新2
如果您仍然喜欢使用,ResultReceiver请查看此主题Robert的答案.它看起来比hacky上下文操作更简洁,更简单.