2010-07-01 2 views
22

Итак, я написал Сервис и деятельность для ОС Android.Android RemoteExceptions and Services

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

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

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

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

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

try { 

    mService.someMethodCall (someArguments); 

    } catch (RemoteException e) { 

    e.printStackTrace(); 

    } 

Это не твердый код, и я не хочу что-то вроде этого в моей код базы.

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

Вопросов являются:

  • сделать эти исключения либо вышвырнут

  • кто-нибудь когда-нибудь видел такой примерки поймать блок поймать RemoteException?

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

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

ответ

49

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

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

Активность увидит это исключение, если оно использует сервис, запущенный в другом процессе, который умирает в середине выполнения запроса. Я смог доказать это себе, сделав следующие незначительные изменения в Marko Gargenta's AIDLDemo example.

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

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

+1

Hi Tim. Какой хороший ответ! Моя служба работала в другом процессе, и я видел DeadObjectExceptions здесь и там во время стресс-тестирования. Alway, когда я использовал оболочку и убил сервисный процесс, пока работало связанное приложение. Ошибка DeadObjectException всегда была в моем списке дел.Теперь, когда я знаю, что они всего лишь подкласс RemoteException, я знаю, что делать. Большое вам спасибо. Я оставляю вопрос открытым еще на один день, если кто-то еще более проницателен! Cheers, Nils –

+0

@Nils Pipenbrinck Glad Я мог бы помочь. Кроме того, теперь, когда вы приняли мой ответ, могу ли я помешать вам присудить награду? –

+0

Я не знал, что вы должны его наградить. Я думал, что это связано с ответом .. :-) –

1

«Исключения не поддерживаются в разных процессах». RemoteException действительно выбрасываются, но не напрямую. EVERY Исключение, отправленное в удаленную службу во время обработки вызова helpl, приведет к тому, что ваше приложение получит RemoteException.

5

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

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

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

  • SecurityException
  • BadParcelableException
  • IllegalArgumentException
  • NullPointerException
  • IllegalStateException
  • NetworkOnMainThreadException
  • UnsupportedOperationException

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