with servicios onbind ibinder enlazados android service aidl remoteexception

onbind - Android RemoteExceptions y servicios



service binder android (3)

Las "excepciones todavía no son compatibles con los procesos" es la clave aquí. Las Excepciones Remotas son efectivamente lanzadas, pero no directamente. CADA Excepción lanzada en su servicio remoto mientras se maneja una llamada de ayuda resultará en que su aplicación reciba una excepción remota.

Así que he escrito un servicio y una actividad para el sistema operativo Android.

Mi servicio se ejecuta en su propio proceso, por lo que toda la comunicación entre mis Actividades y el Servicio se realiza a través de IPC. Yo uso el mecanismo estándar de Android .aidl para esto.

Hasta ahora todo funciona bien. Sin embargo, la AIDL genera todos los apéndices de métodos usando "throws RemoteException", así que tengo que manejarlos.

Hice un grep rápido en todo el código fuente de Android y solo encontré tres casos en los que se produce esta excepción. Estos están en un servicio diferente con el que no me conecto.

También verifiqué las fuentes C porque, en teoría, se pueden generar RemoteExceptions utilizando la interfaz JNI. No se encontró nada.

Tengo la impresión de que todos los manejan así:

try { mService.someMethodCall (someArguments); } catch (RemoteException e) { e.printStackTrace(); }

Este no es un código sólido, y no quiero algo como esto en mi base de código.

Además de eso: traté de lanzar una RemoteException a través de IPC y todo lo que obtuve fue un seguimiento de la pila y un mensaje de registro del sistema que me dice que las excepciones aún no son compatibles. Mi aplicación nunca vio la excepción y los servicios que lanzaron la excepción terminaron en un estado muy extraño (a mitad de camino) :-(

Las preguntas son:

  • ¿Se lanzan estas excepciones alguna vez?

  • ¿Alguna vez alguien ha visto un bloque try-catch que atrape una RemoteException?

  • ¿Podría ser que no existan y que nos veamos obligados a lidiar con ellos porque los "lanzan RemoteException" son códigos muertos o sobran dentro del compilador AIDL?

Disclamer: No he leído el código fuente completo. Usé Grep para encontrar las ocurrencias de RemoteException, así que es posible que haya perdido algunas debido al uso de diferentes espacios en blanco.


RemoteException se lanza si el proceso que aloja el objeto remoto ya no está disponible, lo que generalmente significa que el proceso se bloqueó.

Sin embargo, el comentario anterior, y también la documentación oficial de Android es errónea acerca de que DeadObjectException es la única excepción devuelta al cliente. Algunos tipos de RuntimeExceptions lanzadas en la implementación de su servicio AIDL se devolverán al cliente y se volverán a generar allí. Si echa un vistazo al método Binder.execTransact (), verá que detecta RuntimeException y pasa unos pocos seleccionados al cliente.

Las RuntimeExceptions que reciben este tratamiento especial se enumeran a continuación. También puede comprobar la parcela.writeException para verificar. Ese método es usado por la clase Binder para reunir la excepción en una Parcela y transferirla de nuevo al cliente, donde se volverá a generar como parte de la Parcel.readException.

  • Excepcion de seguridad
  • BadParcelableException
  • Argumento de excepción ilegal
  • Excepción de puntero nulo
  • IllegalStateException
  • NetworkOnMainThreadException
  • ExcepcionalOperaciónExcepción

Me encontré con este comportamiento por accidente, estaba viendo excepciones inesperadas en el lado del cliente, y mi servicio no se bloqueaba cuando debería en una excepción IllegalStateException. Escritura completa: https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63


Estas excepciones realmente se lanzan y debe escribir la lógica de prueba / captura adecuada para manejar la situación en la que no se completó un método remoto que invocó en un servicio.

En cuanto a su investigación, estuvo en el camino correcto buscando en las fuentes nativas. Lo que puede haber pasado por alto es que android.os.RemoteException es en realidad solo una clase base para otras excepciones relacionadas con Binder y que es una subclase, android.os.DeadObjectException , que se lanza dentro del código nativo de Binder .

Una actividad verá esta excepción si hace uso de un servicio que se ejecuta en otro proceso que muere en medio de realizar una solicitud. Pude probarme esto haciendo los siguientes cambios menores en el ejemplo AIDLDemo de Marko Gargenta .

Primero, asegúrese de que el servicio se ejecute en su propio proceso actualizando el AndroidManifest.xml:

<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.marakana" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name" android:theme="@android:style/Theme.Light"> <activity android:name=".AIDLDemo" android:label="@string/app_name"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <!--ADD THE android:process TAG TO THE SERVICE--> <service android:name=".AdditionService" android:process=":process2"/> </application> <uses-sdk android:minSdkVersion="3" /> </manifest>

Luego modifique el método de add para salir prematuramente:

@Override public IBinder onBind(Intent intent) { return new IAdditionService.Stub() { /** * Implementation of the add() method */ public int add(int value1, int value2) throws RemoteException { Log.d(TAG, String.format("AdditionService.add(%d, %d)", value1, value2)); System.exit(-1); // KILL THE PROCESS BEFORE IT CAN RESPOND return value1 + value2; } }; }

En logcat usted ve el dado del proceso de servicio, la actividad recibe una DeadObjectException y, en última instancia, el sistema reaparece el proceso de servicio.

D/AdditionService( 1379): AdditionService.add(1, 1) I/AndroidRuntime( 1379): AndroidRuntime onExit calling exit(-1) D/Zygote ( 32): Process 1379 exited cleanly (255) I/ActivityManager( 58): Process com.marakana:process2 (pid 1379) has died. W/ActivityManager( 58): Scheduling restart of crashed service com.marakana/.AdditionService in 5000ms D/AIDLDemo( 1372): onClick failed with: android.os.DeadObjectException W/System.err( 1372): android.os.DeadObjectException W/System.err( 1372): at android.os.BinderProxy.transact(Native Method) W/System.err( 1372): at com.marakana.IAdditionService$Stub$Proxy.add(IAdditionService.java:95) W/System.err( 1372): at com.marakana.AIDLDemo$1.onClick(AIDLDemo.java:81) W/System.err( 1372): at android.view.View.performClick(View.java:2408) W/System.err( 1372): at android.view.View$PerformClick.run(View.java:8816) W/System.err( 1372): at android.os.Handler.handleCallback(Handler.java:587) W/System.err( 1372): at android.os.Handler.dispatchMessage(Handler.java:92) W/System.err( 1372): at android.os.Looper.loop(Looper.java:123) W/System.err( 1372): at android.app.ActivityThread.main(ActivityThread.java:4627) W/System.err( 1372): at java.lang.reflect.Method.invokeNative(Native Method) W/System.err( 1372): at java.lang.reflect.Method.invoke(Method.java:521) W/System.err( 1372): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) W/System.err( 1372): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) W/System.err( 1372): at dalvik.system.NativeStart.main(Native Method) D/AIDLDemo( 1372): onServiceDisconnected() disconnected I/ActivityManager( 58): Start proc com.marakana:process2 for service com.marakana/.AdditionService: pid=1399 uid=10037 gids={1015} D/AdditionService( 1399): onCreate() D/AIDLDemo( 1372): onServiceConnected() connected

Me imagino que si su servicio se estuviera ejecutando en el mismo proceso que su actividad, es posible que nunca vea esta excepción, pero nuevamente, si ese fuera el caso, probablemente no se molestaría con AIDL.

Además, como descubrió, Android no canaliza excepciones entre procesos. Si necesita comunicar un error a una actividad de llamada, debe utilizar otros medios.