代码之家  ›  专栏  ›  技术社区  ›  Nils Pipenbrinck

Android远程例外和服务

  •  23
  • Nils Pipenbrinck  · 技术社区  · 14 年前

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

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

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

    我对整个android源代码做了一个快速的grep,只发现了三个曾经抛出这个异常的案例。这些服务与我没有联系。

    我也检查了C源,因为理论上可以使用JNI接口生成远程异常。什么也没出现。

    我觉得每个人都是这样处理的:

      try {
    
        mService.someMethodCall (someArguments);
    
      } catch (RemoteException e) {
    
        e.printStackTrace();
    
      }
    

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

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

    问题是:

    • 这些异常会被抛出吗?

    • 有人见过这样一个try-catch块捕获一个remoteexception吗?

    • 可能是因为它们不存在,我们只是被迫处理它们,因为“throwsRemoteException”是死代码,或者是AIDL编译器中的遗留代码?

    披露者:我还没有阅读完整的源代码。我使用grep来查找出现的remoteexception,因此可能由于使用了不同的空白而错过了一些。

    3 回复  |  直到 8 年前
        1
  •  54
  •   Tim Kryger    14 年前

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

    至于你的调查,你是在正确的轨道上通过本地来源。你可能忽略了的是 android.os.RemoteException 实际上只是其他绑定器相关异常的一个基类,它是一个子类, android.os.DeadObjectException ,在 native code of Binder .

    如果活动使用在另一个进程中运行的服务,而该进程在执行请求的过程中死亡,那么它将看到此异常。我可以通过对 Marko Gargenta's AIDLDemo example .

    首先,通过更新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
  •  6
  •   Alex Suzuki    8 年前

    如果 过程 托管远程对象不再可用,这通常意味着进程崩溃。

    然而,之前的评论,以及官方的Android文档都是错误的,因为DeadObjectException是唯一一个返回到客户机的异常。在AIDL服务实现中抛出的某些类型的运行时异常将被传递回客户机并在那里重新调用。如果您查看binder.exectransact()方法,您将看到它捕获runtimeexception并将一些select返回到客户机。

    下面列出了接受此特殊处理的RuntimeExceptions。您还可以检查package.writeException进行验证。绑定器类使用该方法将异常封送到一个包中,并将其传输回客户端,在客户端中,它将作为parcel.readException的一部分重新引发。

    • 安全例外
    • BadparcelableException(BadparcelableException)
    • 非法数据异常
    • 空指针异常
    • IllegalStateException
    • 网络维护例外
    • UnsupportedOperation异常

    我偶然发现了这种行为,我在客户机端看到了意外的异常,而我的服务没有崩溃,当它应该出现非法状态异常时。完全写入: https://blog.classycode.com/dealing-with-exceptions-in-aidl-9ba904c6d63

        3
  •  1
  •   Sweaty Techie    14 年前

    这里的关键是“异常尚不支持跨进程”。确实会抛出RemoteException,但不会直接抛出。 每个 处理AIDL调用时在远程服务中引发的异常将导致应用程序接收到远程异常。