Android RemoteExceptions and Services

Поэтому я написал Сервис и деятельность для ОС Android.

Моя служба работает в собственном процессе, поэтому вся связь между моими действиями и службой происходит через IPC. Для этого я использую стандартный механизм Android .aidl.

Пока все работает нормально. Тем не менее, AIDL генерирует все заглушки методов, используя «throws RemoteException», поэтому мне приходится их обрабатывать.

Я сделал быстрый grep на весь исходный код Android и нашел только три случая, когда это исключение когда-либо бросали. Они находятся в другой службе, с которой я не связан.

Я также проверил C-источники, потому что теоретически RemoteExceptions могут быть сгенерированы с использованием интерфейса JNI. Ничего не появилось.

У меня создалось впечатление, что все просто так обрабатывают их:

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

Это не сплошной код, и я не хочу, чтобы что-то подобное в моей базе кода.

Кроме того: я попытался запустить RemoteException через IPC самостоятельно, и все, что у меня было, это трассировка стека и сообщение системного журнала, которое сообщает мне, что исключения еще не поддерживаются. В моем приложении никогда не было исключения, а службы, которые выбрасывали исключение, оказались в очень странном состоянии (на полпути) 🙁

Вопросы:

  • Разве эти исключения когда-либо бросаются?

  • Кто-нибудь видел такой блок try-catch, улавливающий RemoteException?

  • Может быть, они не существуют и что мы просто вынуждены иметь дело с ними, потому что «throws RemoteException» является мертвым кодом или левым в компиляторе AIDL?

Disclamer: Я не читал весь исходный код. Я использовал Grep, чтобы найти случаи RemoteException, поэтому я, возможно, пропустил некоторые из-за использования разных пробелов.

Solutions Collecting From Web of "Android RemoteExceptions and Services"

Эти исключения действительно бросаются, и вы должны написать соответствующую логику try / catch для обработки ситуации, когда удаленный метод, который вы вызывали в службе, не был завершен.

Что касается вашего расследования, вы были на правильном пути, просматривая родные источники. Что вы, возможно, упустили, так это то, что android.os.RemoteException на самом деле является просто базовым классом для других связанных с Binder исключений и что это подкласс android.os.DeadObjectException , который вызывается внутри нативного кода Binder .

Это событие увидит это событие, если оно использует сервис, запущенный в другом процессе, который умирает в середине выполнения запроса. Я смог доказать это сам, сделав следующие незначительные изменения в примере AIDLDemo Марко Гаргенты .

Во-первых, убедитесь, что служба запускается в своем собственном процессе, обновив 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> 

Затем измените метод add для выхода преждевременно:

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

В logcat вы видите, что процесс службы умирает, а активность получает DeadObjectException , и в конечном итоге система DeadObjectException процесс обслуживания.

 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 

Я бы предположил, что если ваша служба работает в том же процессе, что и ваша деятельность, вы, возможно, никогда не увидите это исключение, но опять же, если это так, вы, вероятно, не потрудились бы с AIDL.

Кроме того, как вы обнаружили, Android не туннелирует исключения между процессами. Если вам нужно сообщить об ошибке обратно в вызывающую деятельность, вам необходимо использовать другие средства.

RemoteException выдается, если процесс размещения удаленного объекта больше недоступен, что обычно означает, что процесс разбился.

Тем не менее, предыдущий комментарий, а также официальная документация на Android неправильны в том, что DeadObjectException является единственным исключением, когда-либо возвращенным клиенту. Некоторые типы RuntimeExceptions, введенные в вашу реализацию службы AIDL, будут переданы обратно клиенту и возвращены туда. Если вы посмотрите на метод Binder.execTransact (), вы увидите, что он ловит RuntimeException и передает несколько назад клиенту.

Представления RuntimeExceptions, которые получают это специальное лечение, перечислены ниже. Вы также можете проверить Parcel.writeException для проверки. Этот метод используется классом Binder для маршализации исключения в парцеллу и передачи его обратно клиенту, где он будет возвращен как часть Parcel.readException.

  • SecurityException
  • BadParcelableException
  • IllegalArgumentException
  • Исключение нулевого указателя
  • IllegalStateException
  • NetworkOnMainThreadException
  • UnsupportedOperationException

Я случайно наткнулся на это поведение, я видел неожиданные исключения на стороне клиента, и моя служба не терпела крах, когда это должно происходить при исключении IllegalStateException. Полная запись: https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63

«Исключения пока не поддерживаются в разных процессах» – вот ключ. RemoteException действительно запущены, но не напрямую. КАЖДЫЙ Исключение, отправленное в удаленную службу во время обработки вызова helpl, приведет к тому, что ваше приложение получит RemoteException.