Android跨进程异常分析小结

通常,我们编写运行Android时,该程序若在一个进程中。若程序有缺陷,如空指针,将会产生空指针异常。 当异常发生时,其调用堆栈将会输出到log中,通过查看调用堆栈,很快可以找到问题的根源,从而解决问题。

但当我们使用Service,通过AIDL跨进程调用(或直接编写Binder IPC的跨进程调用)时,需要使用Parcel跨进程传递数据。有时这种跨进程调用会产生异常,这时问题稍显复杂一些。一个跨进程异常log示例如下:

07-17 16:28:53.969: V/DemoActivity3.MySessionListener(21000): onLeftSession.
07-17 16:28:54.019: D/dalvikvm(21000): GC_CONCURRENT freed 661K, 54% free 3108K/6727K, external 1657K/2137K, paused 3ms+2ms
07-17 16:28:55.769: I/DemoActivity3(21000): mLogoutButton clicked
07-17 16:28:55.779: D/AndroidRuntime(21000): Shutting down VM
07-17 16:28:55.779: W/dalvikvm(21000): threadid=1: thread exiting with uncaught exception (group=0×40015560)
07-17 16:28:55.789: E/AndroidRuntime(21000): FATAL EXCEPTION: main
07-17 16:28:55.789: E/AndroidRuntime(21000): java.lang.SecurityException

07-17 16:28:55.789: E/AndroidRuntime(21000): at android.os.Parcel.readException(Parcel.java:1322)
07-17 16:28:55.789: E/AndroidRuntime(21000): at android.os.Parcel.readException(Parcel.java:1276)
07-17 16:28:55.789: E/AndroidRuntime(21000): at com.redwolfsoft.xxx.sdk3.internal.ISessionService$Stub$Proxy.sendAction(ISessionService.java:320)
07-17 16:28:55.789: E/AndroidRuntime(21000): at com.redwolfsoft.xxx.sdk3.SessionManager.leaveSession(SessionManager.java:351)
07-17 16:28:55.789: E/AndroidRuntime(21000): at ccom.redwolfsoft.xxx.sdkdemo3.DemoActivity3$5.onClick(DemoActivity3.java:602)
07-17 16:28:55.789: E/AndroidRuntime(21000): at android.view.View.performClick(View.java:2485)
07-17 16:28:55.789: E/AndroidRuntime(21000): at android.view.View$PerformClick.run(View.java:9080)
07-17 16:28:55.789: E/AndroidRuntime(21000): at android.os.Handler.handleCallback(Handler.java:587)
07-17 16:28:55.789: E/AndroidRuntime(21000): at android.os.Handler.dispatchMessage(Handler.java:92)
07-17 16:28:55.789: E/AndroidRuntime(21000): at android.os.Looper.loop(Looper.java:130)
07-17 16:28:55.789: E/AndroidRuntime(21000): at android.app.ActivityThread.main(ActivityThread.java:3683)
07-17 16:28:55.789: E/AndroidRuntime(21000): at java.lang.reflect.Method.invokeNative(Native Method)
07-17 16:28:55.789: E/AndroidRuntime(21000): at java.lang.reflect.Method.invoke(Method.java:507)
07-17 16:28:55.789: E/AndroidRuntime(21000): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:839)
07-17 16:28:55.789: E/AndroidRuntime(21000): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
07-17 16:28:55.789: E/AndroidRuntime(21000): at dalvik.system.NativeStart.main(Native Method)
07-17 16:29:10.069: D/dalvikvm(21009): GC_EXPLICIT freed 688K, 52% free 3013K/6151K, external 1625K/2137K, paused 52ms

 

通过查看android.os.Parcel.readException源码实现,被调用侧(Server侧)的这些异常的编码号code和异常描述信息,在被Parcel读取后,将在调用侧抛出对应类型的异常。

因此,这类因跨进程产生的异常,将被跨进程送到调用侧(Client侧)。也就是说,问题的根源来自Server侧的代码,若被调用侧遇到程序崩溃的异常,如NullointerExeption或SecurityException。
将在调用侧产生一个异常,这个异常由Parcel读取,在Client侧被抛出。

这种只输出调用侧的调用堆栈,如何去分析并解决它呢?由于没有输出被调用侧的堆栈,所以问题根源不那么明显。

根据笔者经验,若是自己直接编写的Binder IPC子类(没有使用AIDL的形式),则首先检查调用侧的IXXX.Stub.Proxy中的通过Parcel参数写入是否正确,然后再检查IXXX.Stub中的switch-case分支中Parcel读取时,读取的顺序以及参数类型是否正确。

在确认无误后,再仔细检查被调用侧IXXX.Stub的子类实现的各函数的代码,如空指针(值为null并在未赋值时使用其成员);若是上述示例中的安全异常SecurityException,则需检查未使用new分配内存即直接使用,或释放后仍在使用,类似于C++中对无效内存的访问。尤其是被调用的Server侧有类的实例对象的变量,它们将是被重点怀疑的对象,通过这些实例对象变量使用成员变量值或成员函数的代码,最有可能是问题的根源,这时就要检查类的实例是否初始化,是否分配了内存,其生命周期如何(何时被创建,何时被销毁)。通过分析检查代码,应该就可以很快找到问题的根源。

另外一种不是轻快好省的方式是,可以将这种跨进程代码移植到一个进程中,保证模拟出出问题时的场景,让程序输出崩溃时的调用堆栈。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值