Подтвердить что ты не робот

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

4b9b3361

Ответ 1

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

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

Ответ 2

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

Однако предыдущий комментарий, а также официальная документация на 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

Ответ 3

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