34

所以我为 Android 操作系统编写了一个服务和一个活动。

我的服务在它自己的进程中运行,所以我的活动和服务之间的所有通信都是通过 IPC 进行的。为此,我使用标准的 Android .aidl 机制。

到目前为止一切正常。但是,AIDL 使用“throws RemoteException”生成所有方法存根,所以我必须处理它们。

我对整个 Android 源代码进行了快速 grep,只发现了三个抛出此异常的情况。这些在我没有联系的不同服务中。

我也检查了 C 源代码,因为理论上 RemoteExceptions 可以使用 JNI 接口生成。没有出现。

我的印象是每个人都只是这样处理它们:

  try {

    mService.someMethodCall (someArguments);

  } catch (RemoteException e) {

    e.printStackTrace();

  }

这不是可靠的代码,我不希望在我的代码库中有这样的东西。

除此之外:我自己尝试通过 IPC 抛出 RemoteException,我得到的只是一个堆栈跟踪和一条系统日志消息,告诉我还不支持异常。我的应用程序从未看到异常,并且引发异常的服务最终处于非常奇怪的状态(中途工作):-(

问题是:

  • 这些异常会被抛出吗?

  • 有没有人见过这样的 try-catch 块捕获 RemoteException?

  • 难道它们不存在,我们只是被迫处理它们,因为“抛出 RemoteException”是死代码或 AIDL 编译器内的遗留代码?

Disclamer:我还没有阅读完整的源代码。我使用 Grep 来查找 RemoteException 的出现,所以我可能由于不同的空白使用而错过了一些。

4

3 回答 3

64

这些异常确实会被抛出,您应该编写适当的 try/catch 逻辑来处理您在服务上调用的远程方法未完成的情况。

就您的调查而言,您在查看本地资源时走在正确的轨道上。您可能忽略的是,android.os.RemoteException它实际上只是其他 Binder 相关异常的基类,它是一个子类,它是在Binder 的本机代码中android.os.DeadObjectException抛出的。

如果一个活动使用在另一个进程中运行的服务,该服务在执行请求的过程中死掉了,它将看到这个异常。通过对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 中,您会看到服务进程死亡,活动收到 a 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 不会在进程之间隧道异常。如果您需要将错误传达回调用活动,那么您需要使用其他方式。

于 2010-07-05T03:56:27.550 回答
13

如果托管远程对象的进程不再可用,则抛出 RemoteException ,这通常意味着进程崩溃。

然而,之前的评论以及官方的 Android 文档都认为 DeadObjectException 是唯一一个被抛出回客户端的异常是错误的。在您的 AIDL 服务实现中抛出的某些类型的 RuntimeExceptions 将被传递回客户端并在那里重新抛出。如果您看一下 Binder.execTransact() 方法,您会发现它捕获了 RuntimeException 并将选择的几个返回给客户端。

下面列出了接受这种特殊处理的 RuntimeExceptions。你也可以检查 Parcel.writeException 来验证。Binder 类使用该方法将异常编组到 Parcel 中并将其传输回客户端,在那里它将作为 Parcel.readException 的一部分重新抛出。

  • 安全异常
  • BadParcelableException
  • IllegalArgumentException
  • 空指针异常
  • 非法状态异常
  • NetworkOnMainThreadException
  • 不支持的操作异常

我偶然发现了这种行为,我在客户端看到了意外的异常,并且我的服务在 IllegalStateException 上应该没有崩溃。完整文章: https ://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63

于 2017-01-08T12:47:59.597 回答
2

“跨进程尚不支持异常”是这里的关键。确实会抛出 RemoteException,但不是直接在发生错误时抛出。在处理辅助调用时,在远程服务中抛出的每个未捕获的 RuntimeException 都会导致应用程序接收到异常,而那些未显式传递的异常则会触发通用 RemoteException(告诉您远程进程中发生异常) .

我建议在您的服务中为每个 binder 方法实现设置通用异常捕获块,并在此时决定是让服务继续运行还是终止它,并返回错误代码或抛出异常(可能是支持传递的异常代码)跨IPC)。

于 2011-02-10T19:57:12.360 回答