Android RemoteExceptions 和服务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3156389/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 08:57:24  来源:igfitidea点击:

Android RemoteExceptions and Services

androidserviceaidlremoteexception

提问by Nils Pipenbrinck

So I've written a Service and an Activity for the Android OS.

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

My service is running in it's own process, so all the communication between my Activities and the Service happens via IPC. I use the standard Android .aidl mechanism for this.

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

So far everything works fine. However, the AIDL generates all method stubs using "throws RemoteException" so I have to handle them.

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

I did a quick grep on the entire Android source-code and only found three cases where this exception is ever thrown. These are in a different service that I don't connect with.

我对整个 Android 源代码做了一个快速的 grep,只发现了三个抛出这个异常的情况。这些位于我未连接的不同服务中。

I checked the C-sources as well because in theory RemoteExceptions can be generated using the JNI interface.. Nothing turned up.

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

I have the impression that everyone just handles them like this:

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

  try {

    mService.someMethodCall (someArguments);

  } catch (RemoteException e) {

    e.printStackTrace();

  }

This is not solid code, and I don't want something like this in my code-base.

这不是可靠的代码,我不想在我的代码库中出现这样的东西。

Besides that: I tried to throw a RemoteException via IPC myself and all I got was a stack-trace and a system log message that tells me that exceptions aren't supported yet. My application never saw the exception and the services that threw the exception ended up in a very strange state (halfway working) :-(

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

The questions are:

问题是:

  • Do these exceptions ever get thrown?

  • Has anyone ever seen such a try-catch block catching a RemoteException?

  • Could it be that they don't exist and that we are just forced to deal with them because the "throws RemoteException" is dead code or a left-over inside the AIDL compiler?

  • 这些异常会被抛出吗?

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

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

Disclamer: I haven't read the entire source-code. I used Grep to find the occurrences of RemoteException, so I may have missed some due to different whitespace usage.

免责声明:我还没有阅读整个源代码。我使用 Grep 来查找 RemoteException 的出现,因此由于不同的空格使用,我可能错过了一些。

回答by Tim Kryger

These exceptions do indeed get thrown and you should write appropriate try/catch logic to handle the situation where a remote method you invoked on a service did not complete.

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

As far as your investigation, you were on the right track looking through the native sources. What you may have overlooked is that android.os.RemoteExceptionis a actually just a base class for other Binder related exceptions and that it is a subclass, android.os.DeadObjectException, which is thrown within the native code of Binder.

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

An activity will see this exception if it makes use of a service running in another process that dies in the middle of performing a request. I was able to prove this to myself by making the following minor changes to Marko Gargenta's AIDLDemo example.

如果 Activity 使用在另一个进程中运行的服务,该服务在执行请求的过程中终止,则该活动将看到此异常。通过对Marko Gargenta 的 AIDLDemo 示例进行以下细微更改,我能够向自己证明这一点。

First, make sure the service runs in its own process by updating the AndroidManifest.xml:

首先,通过更新 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> 

Then modify the addmethod to exit prematurely:

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

    };
}

In logcat you see the service process die, the activity receive a DeadObjectException, and ultimately the system respawn the service process.

在 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.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

I would imagine if your service was running in the same process as your activity you might never see this exception but then again if that were the case you probably wouldn't be bothering with AIDL.

我想如果您的服务与您的活动在同一进程中运行,您可能永远不会看到此异常,但如果是这种情况,您可能不会再为 AIDL 烦恼。

Additionally, as you discovered, Android does not tunnel exceptions between processes. If you need to communicate an error back to a calling activity then you need to use other means.

此外,正如您所发现的,Android 不会在进程之间建立隧道异常。如果您需要将错误传达回调用活动,那么您需要使用其他方式。

回答by Alex Suzuki

RemoteException is thrown if the processhosting the remote object is no longer available, which usually means the process crashed.

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

However, the previous comment, and also the official Android documentation is wrong about DeadObjectException being the only exception ever thrown back to the client. Some types of RuntimeExceptions thrown in your AIDL service implementation will be passed back to the client and rethrown there. If you take a look at the Binder.execTransact() method, you will see that it catches RuntimeException and passes a select few back to the client.

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

The RuntimeExceptions that receive this special treatment are listed below. You can also check Parcel.writeException to verify. That method is used by the Binder class to marshal the exception into a Parcel and transfer it back to the client, where it will be rethrown as part of Parcel.readException.

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

  • SecurityException
  • BadParcelableException
  • IllegalArgumentException
  • NullPointerException
  • IllegalStateException
  • NetworkOnMainThreadException
  • UnsupportedOperationException
  • 安全异常
  • 坏包裹异常
  • 非法参数异常
  • 空指针异常
  • 非法状态异常
  • 网络主线程异常
  • 不支持的操作异常

I stumbled on this behavior by accident, I was seeing unexpected exceptions on the client side, and my service was not crashing when it should on an IllegalStateException. Full writeup: https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63

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

回答by Sweaty Techie

The "Exceptions are not yet supported across processes" is the key here. RemoteException's are indeed thrown, but not directly at the point of the error occurring. EVERYuncaught RuntimeException thrown in your remote service while an aidl call is being handled will result in an Exception being received by your application, and those which are not explicitly passed over instead trigger the generic RemoteException (telling you an exception occurred in the remote process).

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

I recommend having generic exception catch blocks in your service for each binder method implementation, and deciding at that point whether to let the service keep running or kill it, and return an error code or throw the exception (possibly one that is supported as being passed across the IPC).

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