Android RemoteExceptions and Services

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

Mi servicio está funcionando en su propio proceso, así que toda la comunicación entre mis actividades y el servicio ocurre vía IPC. Utilizo el mecanismo Android estándar .aidl para esto.

Hasta ahora todo funciona bien. Sin embargo, el AIDL genera todos los stubs de método utilizando "lanza RemoteException" por lo 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 esta excepción es lanzada. Estos están en un servicio diferente al que no me conecto.

También revisé las fuentes C porque, en teoría, RemoteExceptions se pueden generar utilizando la interfaz JNI. Nada resultó.

Tengo la impresión de que todo el mundo sólo los maneja de esta manera:

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

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

Además: intenté lanzar una RemoteException a través de IPC y todo lo que conseguí fue un stack-trace y un mensaje de registro del sistema que me dice que las excepciones no son compatibles aún. Mi aplicación nunca vio la excepción y los servicios que lanzó la excepción terminó en un estado muy extraño (a mitad de trabajo) 🙁

Las preguntas son:

  • ¿Alguna vez se les arroja alguna excepción?

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

  • ¿Podría ser que no existen y que sólo estamos obligados a tratar con ellos porque el "lanza RemoteException" es código muerto o un sobrante dentro del compilador AIDL?

Disclaimer: No he leído todo el código fuente. Utilicé Grep para encontrar las ocurrencias de RemoteException, así que puede haber faltado alguno debido a diverso uso del whitespace.

Estas excepciones se lanzan realmente y debe escribir apropiada try / catch lógica para manejar la situación donde un método remoto que invocó en un servicio no se completó.

En cuanto a su investigación, usted estaba en el camino correcto mirando a través de las fuentes nativas. Lo que puede haber pasado por alto es que android.os.RemoteException es realmente una clase base para otras excepciones relacionadas con Binder y que es una subclase, android.os.DeadObjectException , que se arroja 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 demostrar esto a mí mismo haciendo los siguientes cambios menores al ejemplo de AIDLDemo de Marko Gargenta .

En primer lugar, asegúrese de que el servicio se ejecute en su propio proceso mediante la actualización de 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> 

A continuación, modifique el método 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 verá el proceso de servicio mueren, la actividad recibe una DeadObjectException , y finalmente el sistema respawn 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 ejecuta en el mismo proceso que su actividad que nunca podría ver esta excepción, pero de nuevo, si ese fuera el caso, probablemente no se molestaría con AIDL.

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

RemoteException se activa si el proceso que aloja el objeto remoto ya no está disponible, lo que normalmente significa que el proceso se ha producido un error.

Sin embargo, el comentario anterior, y también la documentación oficial de Android está equivocado acerca de DeadObjectException siendo la única excepción jamás arrojado de vuelta al cliente. Algunos tipos de RuntimeExceptions arrojados en la implementación del servicio AIDL se devolverán al cliente y se volverán a activar allí. Si echa un vistazo al método Binder.execTransact (), verá que capta RuntimeException y pasa unos select back al cliente.

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

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

Me tropecé en este comportamiento por accidente, yo estaba viendo excepciones inesperadas en el lado del cliente, y mi servicio no se estrellaba cuando debería en un IllegalStateException. Escritura completa: https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63

Las "excepciones aún no son compatibles con todos los procesos" es la clave aquí. RemoteException es realmente lanzado, pero no directamente. CADA Excepción lanzada en su servicio remoto mientras se está tratando una llamada de ayuda resultará en una RemoteException recibida por su aplicación.

  • AlarmManager o Servicio
  • Android: Pasar variables a un servicio ya en ejecución
  • Servicio de Android interactuando con múltiples actividades
  • Evitar que algunos usuarios cierren el servicio de Android
  • Servicio enlazado versus servicio iniciado en Android y cómo hacer ambas cosas
  • Llamar el método de clase de actividad de la clase de servicio
  • EventBus: la actividad no recibe un evento cuando la aplicación está en segundo plano
  • Inicio del servicio en el arranque sin actividad
  • Cómo utilizar Android LocationManager y Listener
  • Análisis de fondo wifi en Android
  • ¿Cómo se declara un servicio de intención en el manifiesto de Android?
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.