Android RemoteExceptions和服务

Nil*_*nck 26 service android aidl remoteexception

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

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

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

我对整个Android源代码做了快速的grep,并且只发现了三个抛出此异常的情况.这些是我不联系的不同服务.

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

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

  try {

    mService.someMethodCall (someArguments);

  } catch (RemoteException e) {

    e.printStackTrace();

  }
Run Code Online (Sandbox Code Playgroud)

这不是可靠的代码,我不想在我的代码库中使用这样的东西.

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

问题是:

  • 是否会抛出这些异常?

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

  • 难道它们不存在而且我们只是被迫处理它们,因为"抛出RemoteException"是死代码还是AIDL编译器内部的遗留问题?

免责声明:我还没有阅读完整的源代码.我使用Grep来查找RemoteException的出现,所以我可能因为不同的空格使用而错过了一些.

Tim*_*ger 54

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

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

如果活动使用在执行请求过程中死亡的另一个进程中运行的服务,则活动将看到此异常.通过对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> 
Run Code Online (Sandbox Code Playgroud)

然后修改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;
        }

    };
}
Run Code Online (Sandbox Code Playgroud)

在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
Run Code Online (Sandbox Code Playgroud)

我想如果你的服务在与你的活动相同的过程中运行,你可能永远不会看到这个异常,但如果是这样的话,你可能不会对AIDL感到烦恼.

此外,正如您所发现的,Android不会在进程之间传递异常.如果您需要将错误传回给调用活动,那么您需要使用其他方法.

  • 嗨蒂姆。多么好的答案啊!我的服务在不同的进程中运行,并且在压力测试期间我到处都看到了 DeadObjectException。当我使用 shell 并在绑定的应用程序正在运行时终止服务进程时,总是如此。DeadObjectException 一直在我的待办事项列表中。现在我知道它们只是 RemoteException 的子类,我知道该怎么做..非常感谢。我把这个问题留到另一天再讨论,以防有人更有洞察力!干杯,尼尔斯 (2认同)

Ale*_*uki 6

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

但是,之前的评论以及官方的Android文档都是错误的,因为DeadObjectException是唯一被抛回客户端的例外.在您的AIDL服务实现中抛出的某些类型的RuntimeExceptions将被传递回客户端并在那里重新抛出.如果您查看Binder.execTransact()方法,您将看到它捕获RuntimeException并将选择的几个传递回客户端.

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

  • 抛出:SecurityException
  • BadParcelableException
  • 抛出:IllegalArgumentException
  • 空指针异常
  • IllegalStateException异常
  • NetworkOnMainThreadException
  • UnsupportedOperationException异常

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