一、问题背景
最近测试跑monkey报了一个应用崩溃的log,核心堆栈如下(已脱敏,出问题的android系统版本是api11,AndroidR):
ps: 本次涉及的应用包名统一用com.my.app代替
11-28 03:57:20.326 12039 12039 E AndroidRuntime: FATAL EXCEPTION: main
11-28 03:57:20.326 12039 12039 E AndroidRuntime: Process: com.my.app, PID: 12039
11-28 03:57:20.326 12039 12039 E AndroidRuntime: android.app.RemoteServiceException: can't deliver broadcast
11-28 03:57:20.326 12039 12039 E AndroidRuntime: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2073)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: at android.os.Handler.dispatchMessage(Handler.java:111)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: at android.os.Looper.loop(Looper.java:250)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: at android.app.ActivityThread.main(ActivityThread.java:7860)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: at java.lang.reflect.Method.invoke(Native Method)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:591)
11-28 03:57:20.326 12039 12039 E AndroidRuntime: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1123)
网上查了一番,发现有一些前人的分析:
- viewpager嵌套fragment或其他大量动态广播注册注销出现的崩溃: https://blog.csdn.net/qq_27381325/article/details/82811079
咋一眼看着像是系统问题,刚开始抛给系统看了,结果系统讲这很可能是应用成使用广播传输的数据量大导致framework层报出的异常。
不过可以初步看到RemotionServiceException可以先推测跟binder跨进程服务有关。
这里错误message的提醒是传递广播不正确,看下sendBroadcast()方法的定义,确实是有可能抛出RemoteServiceException异常。
android.app.ContextImpl#sendBroadcast(android.content.Intent)源码定义:
@Overridepublic void sendBroadcast(Intent intent) {warnIfCallingFromSystemProcess();String resolvedType = intent.resolveTypeIfNeeded(getContentResolver());try {intent.prepareToLeaveProcess(this);ActivityManager.getService().broadcastIntentWithFeature(mMainThread.getApplicationThread(), getAttributionTag(), intent, resolvedType,null, Activity.RESULT_OK, null, null, null, AppOpsManager.OP_NONE, null, false,false, getUserId());} catch (RemoteException e) { // 这里有声明抛出RemoteException异常,说明发送广播本身就可能抛出异常。throw e.rethrowFromSystemServer();}}
android.os.RemoteException#rethrowFromSystemServer:
@NonNullpublic RuntimeException rethrowFromSystemServer() {if (this instanceof DeadObjectException) {throw new RuntimeException(new DeadSystemException());} else {throw new RuntimeException(this);}}
再看RemoteExcetion和本次的RemoteServiceException有没有继承关系,发现是没有的,看来这里的异常声明和这次崩溃的堆栈可能关系不大,以下是类的继承关系:
发现和RemoteServiceException没有继承关系。
然后想要知道这个异常从哪儿报出来的,通过https://cs.android.com/直接搜索“can’t deliver broadcast”,发现能搜出来,google的源码查看网站果然强大,瞬间感觉了有了源码查看利器!
简单遴选下,发现这个错误最终是在BroadcastQueue中报出来的。
com.android.server.am.BroadcastQueue#performReceiveLocked:
void performReceiveLocked(ProcessRecord app, IIntentReceiver receiver,Intent intent, int resultCode, String data, Bundle extras,boolean ordered, boolean sticky, int sendingUser)throws RemoteException {// Send the intent to the receiver asynchronously using one-way binder calls.if (app != null) {if (app.thread != null) {// If we have an app thread, do the call through that so it is// correctly ordered with other one-way calls.try {app.thread.scheduleRegisteredReceiver(receiver, intent, resultCode,data, extras, ordered, sticky, sendingUser, app.getReportedProcState());// TODO: Uncomment this when (b/28322359) is fixed and we aren't getting// DeadObjectException when the process isn't actually dead.//} catch (DeadObjectException ex) {// Failed to call into the process. It's dying so just let it die and move on.// throw ex;} catch (RemoteException ex) {// Failed to call into the process. It's either dying or wedged. Kill it gently.synchronized (mService) {Slog.w(TAG, "Can't deliver broadcast to " + app.processName+ " (pid " + app.pid + "). Crashing it.");// 这里通过scheduleCrash抛出去的message跟本次要追踪的吻合。// 可以确定就是从这里抛出去的异常。app.scheduleCrash("can't deliver broadcast");}throw ex;}} else {// Application has died. Receiver doesn't exist.throw new RemoteException("app.thread must not be null");}} else {receiver.performReceive(intent, resultCode, data, extras, ordered,sticky, sendingUser);}}
com.android.server.am.ProcessRecord#scheduleCrash:
可以看到ProcessRecord#scheduleCrash()是通过ApplicationThread抛给了应用层,ApplicationThread接口是SystemServer进程和应用进程通信的AIDL接口,定义在ActivityThread.java文件中。
void scheduleCrash(String message) {// Checking killedbyAm should keep it from showing the crash dialog if the process// was already dead for a good / normal reason.if (!killedByAm) {if (thread != null) {if (pid == Process.myPid()) {Slog.w(TAG, "scheduleCrash: trying to crash system process!");return;}long ident = Binder.clearCallingIdentity();try {thread.scheduleCrash(message);} catch (RemoteException e) {// If it's already dead our work is done. If it's wedged just kill it.// We won't get the crash dialog or the error reporting.kill("scheduleCrash for '" + message + "' failed",ApplicationExitInfo.REASON_CRASH, true);} finally {Binder.restoreCallingIdentity(ident);}}}}
android.app.ActivityThread.ApplicationThread#scheduleCrash:
通过该方法将framework层system_server进程的异常传递给了应用进程,并通过Handler的方式跑到了主线程。
public void scheduleCrash(String msg) {sendMessage(H.SCHEDULE_CRASH, msg);
}
由此,堆栈是能对应上了。