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:
- ¿Cómo puedo notificar una actividad en ejecución de un receptor de difusión?
- Cómo realizar la comprobación nula IPC manual
- ¿Cómo transferir archivos entre aplicaciones de Android que se ejecutan en el mismo dispositivo?
- Java.io.IOException: write failed: EPIPE (Broken pipe)
- ¿Qué ocurre cuando la actividad se bloquea?
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?
- Usar SharedPreferences en modo multiproceso
- ¿Cómo llamar a un servicio remoto Android (IPC) desde un servicio Widget / local?
- BindService () devuelve false pero unbindService () necesita ser llamado?
- La mejor manera de realizar la comunicación entre actividades en una aplicación Android TabHost
- Cómo obtener datos de un servicio sin consolidar en Android
- Handler en Android
- GREF que aumenta / que disminuye en servicio multi-hilo (aidl) - qué significa?
- Android IPC, servicio no obtener instanciado
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!