Android ResultReceiver a través de paquetes

Tengo una actividad en el paquete A (SignerClient), y un servicio en el paquete B (MyService)

El receptor de resultados de la actividad:

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

Inicio del servicio:

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

Extremo de recepción:

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

Hacer esto cuando el cliente y el servidor están en el mismo paquete funciona bien. Pero en este caso tengo:

 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) 

¿Qué me estoy perdiendo? ¿Mi idea es posible?

Sí, tu idea es posible. Se ClassNotFoundException excepción ClassNotFoundException porque intenta unparcel una clase que fue creada en un proceso diferente por un ClassLoader diferente.

ResultReceiver clase ResultReceiver implementa Parcelable interfaz Parcelable que es adecuada para llamadas interproceso (IPC), sin embargo, para leer el objeto en el servicio que necesita para utilizar el mismo ClassLoader, que se utilizó para la creación de objetos en la aplicación cliente. Para obtener ese ClassLoader en el lado del servicio, llame createPackageContext método createPackageContext que pasa el nombre del paquete del cliente y CONTEXT_INCLUDE_CODE | CONTEXT_IGNORE_SECURITY combinación de banderas. Esto devolverá el objeto Context del cliente desde el que se puede obtener el objeto ClassLoader correcto.

Ejemplo:

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

Acabo de usarlo en mi código – funciona como un encanto.

ACTUALIZAR
Sin embargo, sugiero considerar el uso de Messenger lugar de ResultReceiver . Implementa la interfaz Parcelable y no necesita ser extendido por lo que el problema de ClassLoader no es posible. Y también se recomienda en la documentación oficial .

ACTUALIZAR 2
En caso de que todavía prefiere utilizar ResultReceiver echar un vistazo a la respuesta de Robert en este hilo . Parece más limpio y más simple que las manipulaciones contextuales hacky.

Quería utilizar un ResultReceiver través de paquetes y tener que cargar el contexto del otro paquete no me parecía correcto … después de todo, el paquete de recepción no necesita saber la subclase particular de ResultReceiver utilizado, sólo tiene que Ser capaz de llamar a send() y dejar pasar la magia del fichero IPC. La necesidad de tener la subclase particular disponible parece una falla de diseño.

Hay una solución alternativa:

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

Este código convierte su instancia de alguna subclase de ResultReceiver en una instancia de ResultReceiver sí mismo, que todavía envía resultados a su actualReceiver original. El receiverForSending puede ser enviado en un Intent extra a otro paquete y puede ser unmarshalled muy bien.

Por alguna razón, tuve este error solo con android.support.v4.os.ResultReceiver . Cambiar las importaciones a android.os.ResultReceiver hizo que los resultados ipc funcionaran a la vez. No se necesita ninguna solución!

  • ¿Por qué es posible escribir una matriz booleana en un paquete pero no en un booleano?
  • Java.io.IOException: write failed: EPIPE (Broken pipe)
  • Android aidl no puede enlazar al servicio, cuando el host se instala después de que el cliente
  • ¿Por qué AIDL / Messenger está enlazado a un Servicio?
  • Cómo obtener datos de un servicio sin consolidar en Android
  • Cómo realizar la comprobación nula IPC manual
  • La vinculación al servicio que ya fue iniciada por BroadcastReceiver crea una nueva instancia de servicio
  • Android IPC y diferencias de ContentProvider
  • Lanzar una excepción personalizada de un servicio a una actividad
  • Cada actividad en Android es un proceso o una aplicación es un proceso
  • ¿Por qué utilizar parcelable cuando se puede realizar la misma tarea utilizando variables estáticas?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.