2011-04-21 7 views
12

tengo una actividad en el paquete A (SignerClient), y un servicio en el paquete B (MyService)Android ResultReceiver través de paquetes

resultreceiver 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 receptor:

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 me sale:

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 falta? ¿Mi idea es posible?

+0

Dado que este tema es bastante antiguo: esta clase de la biblioteca de soporte se agregó en el paquete 'android.os' de Android Lollipop en adelante, y el problema parece resuelto en el proceso. Asegúrese de importar el correcto ... (ver la respuesta a continuación) –

Respuesta

14

Sí, su idea es posible. La excepción ClassNotFoundException se produce porque está intentando separar una clase que se creó en un proceso diferente por un ClassLoader diferente.

ResultReceiver La interfaz implementa la interfaz Parcelable que es adecuada para llamadas entre procesos (IPC), pero para leer su objeto en el servicio necesita usar el mismo ClassLoader, que se usó para la creación de objetos en la aplicación cliente (es decir, la actividad). Para obtener ClassLoader en el lado del servicio, llame al 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 cual 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; 
} 

yo sólo lo he utilizado en mi código - funciona como un encanto.

ACTUALIZACIÓN
Sin embargo, propongo considerar el uso de Messenger en lugar de ResultReceiver. Implementa la interfaz Parcelable y no necesita extenderse, por lo que el problema de ClassLoader no es posible. Y también es recommended in the official documentation.

ACTUALIZACIÓN 2
En caso de que todavía prefieren utilizar ResultReceiver echar un vistazo a Robert's answer in this thread. Se ve más limpio y simple que las manipulaciones de contexto hacky.

+0

Creo que está equivocado aquí, de acuerdo con http://developer.android.com/reference/android/os/Message.html#getData() se Parece que todavía necesita usar un cargador de clases. –

+0

@MiroKropacek No realmente. En el caso de Messenger, debe configurar un cargador de clases solo para instanciar un objeto pasado de un proceso a otro a través del Messenger (es decir, si envía al Proceso A en el campo 'Message.object' una instancia de clase definida en el Proceso B) . El Messenger en sí mismo no requiere que se establezca el cargador de clases y puede usarse con seguridad para pasar primitivas y objetos de tipos cargados por el cargador de clases del sistema, como String, Point, etc. – Idolon

+0

Los documentos recomiendan IntentService sobre MessengerService (http: // developer. android.com/reference/android/app/IntentService.html), lo que tiene sentido para solicitudes puntuales en las que no necesita una conexión continua. Sin embargo, no tiene sentido que no digan cómo enviar los resultados realmente fuera del caso especial de estar en la misma aplicación. –

18

Quería usar un ResultReceiver en paquetes y tener que cargar el contexto del otro paquete simplemente no me parecía correcto ... después de todo, el paquete receptor no necesita conocer la subclase particular de ResultReceiver utilizada, solo necesita poder llamar al send() y dejar que la magia de la carpeta IPC suceda. Necesitar tener la subclase particular disponible parece un defecto de diseño.

hay una solución:

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 la instancia de alguna subclase de ResultReceiver en una instancia de ResultReceiver en sí, que sin embargo sigue enviando los resultados a su original, actualReceiver.El receiverForSending se puede enviar en un Intent extra a otro paquete y puede desasignarse perfectamente.

+0

¡gracias! Muy buena implementación! De hecho, cargar el contexto de algunos paquetes es una mala idea cuando podemos usar esto. –

+1

Ni siquiera estoy usando IPC y todavía tenía que usar este enfoque. Buena solución ... y bastante desafortunada. – user123321

+0

Funcionó perfectamente en mi escenario de IPC :) –

0

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 de la ipc funcionen a la vez. ¡No se necesita ninguna solución!

+1

'android.os.ResultReceiver' se agregó en la API 3: https://developer.android.com/reference/android/os/ResultReceiver.html – Idolon

+0

Tiene razón, editó mi respuesta. –

Cuestiones relacionadas