web-dev-qa-db-ja.com

Android RemoteExceptions and Services

したがって、Android OSのサービスとアクティビティを作成しました。

私のサービスは独自のプロセスで実行されているため、私のアクティビティとサービスの間のすべての通信はIPCを介して行われます。私はこれに標準のAndroid .aidlメカニズムを使用しています。

これまでのところ、すべてが正常に動作します。ただし、AIDLは「throws RemoteException」を使用してすべてのメソッドスタブを生成するため、それらを処理する必要があります。

Android source-code全体に対して簡単なgrepを実行し、この例外がスローされる3つのケースのみを見つけました。これらは、私が接続していない別のサービスにあります。

理論的にはRemoteExceptionはJNIインターフェースを使用して生成できるので、Cソースもチェックしました。

私は誰もが次のようにそれらを単に扱うという印象を持っています:

  try {

    mService.someMethodCall (someArguments);

  } catch (RemoteException e) {

    e.printStackTrace();

  }

これは堅実なコードではなく、コードベースにこのようなものは必要ありません。

その上:IPC自分でRemoteExceptionをスローしようとしましたが、スタックトレースと、例外がまだサポートされていないことを通知するシステムログメッセージだけが表示されました。アプリケーションは、例外と例外をスローしたサービスは非常に奇妙な状態になりました(途中で機能します):-(

質問は次のとおりです。

  • これらの例外はスローされますか?

  • そのようなtry-catchブロックがRemoteExceptionをキャッチするのを見たことがありますか?

  • それらが存在せず、「throw RemoteException」がデッドコードまたはAIDLコンパイラー内の残り物であるため、それらに対処することを強いられているのでしょうか。

Disclamer:ソースコード全体を読んでいません。私はGrepを使用してRemoteExceptionの発生を見つけました。そのため、空白の使用法が異なるために、いくつかを見逃している可能性があります。

27

これらの例外は実際にスローされます。サービスで呼び出したリモートメソッドが完了しなかった状況を処理するには、適切なtry/catchロジックを記述する必要があります。

あなたの調査に関しては、あなたはネイティブのソースを調べて正しい軌道に乗っていました。あなたが見落としている可能性があるのは Android.os.RemoteException は、実際には他のバインダー関連の例外の単なる基本クラスであり、サブクラスであることを示します Android.os.DeadObjectExceptionBinderのネイティブコード 内でスローされます。

アクティビティーが、要求の実行中に停止した別のプロセスで実行されているサービスを利用する場合、この例外が発生します。 Marko Gargentaの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はプロセス間で例外をトンネルしません。呼び出し元のアクティビティにエラーを返す必要がある場合は、他の方法を使用する必要があります。

54
Tim Kryger

リモートオブジェクトをホストしているprocessが利用できなくなった場合、RemoteExceptionがスローされます。これは通常、プロセスがクラッシュしたことを意味します。

ただし、前のコメントと公式のAndroidドキュメントは、DeadObjectExceptionがこれまでにクライアントにスローされる唯一の例外であるという点で間違っています。AIDLサービス実装でスローされるいくつかのタイプのRuntimeExceptionsが返されます。 Binder.execTransact()メソッドを見ると、RuntimeExceptionをキャッチし、selectをクライアントに返していることがわかります。

この特別な扱いを受けるRuntimeExceptionを以下に示します。 Parcel.writeExceptionを確認して確認することもできます。そのメソッドは、例外をParcelにマーシャリングしてクライアントに転送するためにBinderクラスによって使用され、Parcel.readExceptionの一部として再スローされます。

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

私は偶然この動作に遭遇し、クライアント側で予期しない例外が表示されていました。また、IllegalStateExceptionでサービスがクラッシュすることはありませんでした。完全な記述: https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d6

6
Alex Suzuki

ここで重要なのは、「例外はプロセス全体でまだサポートされていない」ことです。 RemoteExceptionは確かにスローされますが、直接はスローされません。 [〜#〜] every [〜#〜] AIDL呼び出しの処理中にリモートサービスで例外がスローされると、アプリケーションでRemoteExceptionが受信されます。

1
Arrgh