结合源码深入理解Android Crash处理流程

应用程序crash在开发过程中还是很常见的,本文主要是从源码的角度去跟踪下Android对于crash的处理流程。App crash的全称:Application crash。而Crash又分为:native crash和framework crash(包含App Crash)。我们在平时开发的时候对于可能有异常的地方,一般都是用try-catch语句去catch 异常信息,但当没有有效的catch住的时候,就会导致应用crash,此时系统就会进行捕获,并进入到crash的流程。

在《从源码角度看Android系统Zygote进程启动过程》一文中可知:system_server进程和上层应用程序的进程都是Zygote进程fork来的,而在这些进程创建的时候会设置未捕获异常的处理器,当系统中有未捕获的异常时候,就会交给异常器去处理。

无论是system_server进程还是应用程序进程都会在创建的过程中调用RuntimeInit.java的commonInit方法去做一些通用的初始化操作,其中就有设置默认的未捕捉异常处理器UncaughtHandler。

备注:本文将结合Android6.0的源码深入理解Android Crash处理流程

1. Android Crash 处理流程

下面以RuntimeInit.java的commonInit方法为起点对Crash流程进行跟踪分析。

1.1 RuntimeInit.java—>commonInit方法

代码路径:frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

深入到commonInit的方法中:

private static final void commonInit() {if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");//设置默认的未捕获异常处理器Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); //注释1...省略...
}

注释解析:

  • 注释1处就是给要创建的进程设置未捕获异常处理器。setDefaultUncaughtExceptionHandler是将异常处理器的handler对象赋给Thread的成员变量。

下面继续跟踪看UncaughtHandler对象实例化的过程!!!

1.2 RuntimeInit.java—>静态内部类UncaughtHandler

代码路径:frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

深入到静态内部类UncaughtHandler中:

private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {public void uncaughtException(Thread t, Throwable e) {try {//确保crash过程不会重复进入if (mCrashing) return;mCrashing = true;//判断是否为系统进程if (mApplicationObject == null) {//系统进程Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); //注释1} else {//普通进程// 注释2StringBuilder message = new StringBuilder();message.append("FATAL EXCEPTION: ").append(t.getName()).append("\n");final String processName = ActivityThread.currentProcessName();if (processName != null) {message.append("Process: ").append(processName).append(", ");}message.append("PID: ").append(Process.myPid());Clog_e(TAG, message.toString(), e);}// 注释3ActivityManagerNative.getDefault().handleApplicationCrash(mApplicationObject, new ApplicationErrorReport.CrashInfo(e));} catch (Throwable t2) {try {Clog_e(TAG, "Error reporting crash", t2);} catch (Throwable t3) {// Even Clog_e() fails!  Oh well.}} finally {// 注释4Process.killProcess(Process.myPid());System.exit(10);}}
}

注释解析:

  • 注释1处当是系统进程Crash信息时:

    1. 开头是: *** FATAL EXCEPTION IN SYSTEM PROCESS:[线程名]

    2. 输出发生Crash时的调用栈信息

  • 注释2处当是普通应用进程时:

    1. 开头是:FATAL EXCEPTION:[线程名]

    2. 然后是:Process:[进程名],PID:[进程id]

    3. 输出发生Crash时的调用栈信息

  • 注释3处是启动Crash的弹框。而ActivityManagerNative.getDefault()返回的是ActivityManagerProxy(简称AMP),AMP经过binder调用最后会交给ActivityManagerService(简称AMS),即Crash的弹框最终调用的是AMS的handleApplicationCrash方法。其中传入的参数new ApplicationErrorReport.CrashInfo(e)的作用是将crash信息文件名、类名、方法名、对应行号和异常信息封装到CrashInfo对象中。

  • 注释4处彻底杀掉当前进程

备注:

在平时看Crash 的log时,如果是普通进程,我们只需要搜索关键词“FATAL EXCEPTION:”;如果是系统进程,则需要搜索关键词“*** FATAL EXCEPTION IN SYSTEM PROCESS”

1.3 AMS—>handleApplicationCrash方法

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到handleApplicationCrash方法中:

public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {// 注释1ProcessRecord r = findAppProcess(app, "Crash");// 注释2final String processName = app == null ? "system_server": (r == null ? "unknown" : r.processName);// 注释3handleApplicationCrashInner("crash", r, processName, crashInfo);
}

注释解析:

  • 注释1处获取进程的ProcessRecord对象

  • 注释2处获取进程名

    1. 当远程IBinder对象为空时,则进程名为system_server

    2. 当远程IBinder对象不为空时,并且ProcessRecord为空时,则进程名为unknown

    3. 当远程IBinder对象不为空时,并且ProcessRecord不为空时,则进程名为ProcessRecord对象中相应的进程名

  • 注释3处继续调用内部方法handleApplicationCrashInner

1.4 AMS—>handleApplicationCrashInner方法

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到handleApplicationCrashInner方法中:

void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName,ApplicationErrorReport.CrashInfo crashInfo) {// 注释1EventLog.writeEvent(EventLogTags.AM_CRASH, Binder.getCallingPid(),UserHandle.getUserId(Binder.getCallingUid()), processName,r == null ? -1 : r.info.flags,crashInfo.exceptionClassName,crashInfo.exceptionMessage,crashInfo.throwFileName,crashInfo.throwLineNumber);// 注释2addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo);// 注释3crashApplication(r, crashInfo);
}

注释解析:

  • 注释1处将Crash信息写入到Event Log中

  • 注释2处将错误信息添加到DropBox。其中dropbox的Crash信息是输出到/data/system/dropbox目录下,如果是system_server的crash,那么dropbox的文件名就是system_server_crash@xxx.txt(xxx是时间戳)

  • 注释3处继续调用内部方法crashApplication

1.5 AMS—>crashApplication方法

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到crashApplication方法中:

private void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {long timeMillis = System.currentTimeMillis();String shortMsg = crashInfo.exceptionClassName;String longMsg = crashInfo.exceptionMessage;String stackTrace = crashInfo.stackTrace;if (shortMsg != null && longMsg != null) {longMsg = shortMsg + ": " + longMsg;} else if (shortMsg != null) {longMsg = shortMsg;}AppErrorResult result = new AppErrorResult();synchronized (this) {...省略...// 注释1if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace)) {Binder.restoreCallingIdentity(origId);return;}Message msg = Message.obtain();msg.what = SHOW_ERROR_MSG;HashMap data = new HashMap();data.put("result", result);data.put("app", r);msg.obj = data;// 注释2mUiHandler.sendMessage(msg);}...省略...
}

注释解析:

  • 注释1处调用makeAppCrashingLocked,继续处理crash流程

  • 注释2处发送SHOW_ERROR_MSG,弹出crash提示框,等待用户选择

下面继续深入到makeAppCrashingLocked方法中!!!

1.6 AMS—>makeAppCrashingLocked方法

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到makeAppCrashingLocked方法中:

private boolean makeAppCrashingLocked(ProcessRecord app,String shortMsg, String longMsg, String stackTrace) {app.crashing = true;// 注释1app.crashingReport = generateProcessError(app,ActivityManager.ProcessErrorStateInfo.CRASHED, null, shortMsg, longMsg, stackTrace);// 注释2startAppProblemLocked(app);// 注释3app.stopFreezingAllLocked();// 注释4return handleAppCrashLocked(app, "force-crash" /*reason*/, shortMsg, longMsg, stackTrace);
}

注释解析:

  • 注释1处封装crash信息到crashingReport对象中

  • 注释2处调用startAppProblemLocked方法,主要功能是获取当前用户下crash应用的error receiver和忽略当前app的广播接受者

  • 注释3处调用ProcessRecord的stopFreezingAllLocked方法停止屏幕冻结

  • 注释4处调用handleAppCrashLocked方法继续处理crash流程

其中注释1和注释2处的调用流程不是本文的重点,这里就不继续跟踪,大家如果有兴趣,可以自行阅读!后面我会给出一个函数调用链,可供参考。

下面继续跟踪查看注释4处的handleAppCrashLocked方法。

1.7 AMS—>handleAppCrashLocked方法

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入到handleAppCrashLocked方法中:

private boolean handleAppCrashLocked(ProcessRecord app, String reason,String shortMsg, String longMsg, String stackTrace) {long now = SystemClock.uptimeMillis();Long crashTime;if (!app.isolated) {crashTime = mProcessCrashTimes.get(app.info.processName, app.uid);} else {crashTime = null;}// 当同一个进程,连续两次crash的时间间隔小于1分钟时,则认为crash太过于频繁if (crashTime != null && now < crashTime+ProcessList.MIN_CRASH_INTERVAL) {Slog.w(TAG, "Process " + app.info.processName+ " has crashed too many times: killing!");EventLog.writeEvent(EventLogTags.AM_PROCESS_CRASHED_TOO_MUCH,app.userId, app.info.processName, app.uid);mStackSupervisor.handleAppCrashLocked(app); //注释1if (!app.persistent) {EventLog.writeEvent(EventLogTags.AM_PROC_BAD, app.userId, app.uid,app.info.processName);if (!app.isolated) {mBadProcesses.put(app.info.processName, app.uid,new BadProcessInfo(now, shortMsg, longMsg, stackTrace));mProcessCrashTimes.remove(app.info.processName, app.uid);}app.bad = true;app.removed = true;// 移除进程的所有服务removeProcessLocked(app, false, false, "crash"); //注释2// 恢复最顶部的ActivitymStackSupervisor.resumeTopActivitiesLocked(); //注释3return false;}mStackSupervisor.resumeTopActivitiesLocked();} else {// 结束最顶端正在运行的ActivitymStackSupervisor.finishTopRunningActivityLocked(app, reason);}// 运行在当前进程中的所有服务的crash次数执行加1操作for (int i=app.services.size()-1; i>=0; i--) {ServiceRecord sr = app.services.valueAt(i);sr.crashCount++;}...省略...// 当app存在crash的handler,那么交给其处理if (app.crashHandler != null) mHandler.post(app.crashHandler); //注释4return true;
}

注释解析:

  • 注释1处调用ActivityStackSupervisor的handleAppCrashLocked方法继续处理crash流程

  • 注释2处调用内部的removeProcessLocked方法,主要用于移除进程的所有服务

  • 注释3处调用ActivityStackSupervisor的resumeTopActivitiesLocked方法恢复最顶部的Activity

  • 注释4处判断crash的App内部是否有crashHandler,如果有,则交给App内部的crashHandler去处理。这里也证明了《屏蔽Crash 提示框的两种方式》一文中第二种方案使用crashHandler屏蔽crash提示框的可行性。

逻辑分析流程图:

在这里插入图片描述

其中注释2和注释3的流程就不跟踪了,如果有兴趣,自行查阅。后面我会给出一个函数调用链,可供参考。

下面跟踪看下注释1处的处理流程。

1.8 ActivityStackSupervisor.java—>handleAppCrashLocked方法

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityStackSupervisor.java

深入到handleAppCrashLocked方法中:

void handleAppCrashLocked(ProcessRecord app) {for (int displayNdx = mActivityDisplays.size() - 1; displayNdx >= 0; --displayNdx) {final ArrayList stacks = mActivityDisplays.valueAt(displayNdx).mStacks;int stackNdx = stacks.size() - 1;while (stackNdx >= 0) {//调用ActivityStack的handleAppCrashLocked方法stacks.get(stackNdx).handleAppCrashLocked(app);stackNdx--;}}
}

继续深入到ActivityStack.java的handleAppCrashLocked方法中。

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityStack.java

深入到handleAppCrashLocked方法中:

void handleAppCrashLocked(ProcessRecord app) {for (int taskNdx = mTaskHistory.size() - 1; taskNdx >= 0; --taskNdx) {final ArrayList activities = mTaskHistory.get(taskNdx).mActivities;for (int activityNdx = activities.size() - 1; activityNdx >= 0; --activityNdx) {final ActivityRecord r = activities.get(activityNdx);if (r.app == app) {Slog.w(TAG, "  Force finishing activity "+ r.intent.getComponent().flattenToShortString());// Force the destroy to skip right to removal.r.app = null;//结束当前ActivityfinishCurrentActivityLocked(r, FINISH_IMMEDIATELY, false);}}}
}

到这里AMS中的crashApplication方法中调用的makeAppCrashingLocked方法的流程(上面1.5中的注释1)就跟踪完毕!!!

下面继续看下AMS中的crashApplication方法中UiHandler(上面1.5中的注释2)

1.9 AMS—>内部类UiHandler

从上面1.5中的注释2处可知是通过mUiHandler发送message,且消息的msg.what=SHOW_ERROR_MSG,下面来看下mUiHandler中的handleMessage处理过程。

代码路径:frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

深入内部类UiHandler中:

final class UiHandler extends Handler {public UiHandler() {super(com.android.server.UiThread.get().getLooper(), null, true);}@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case SHOW_ERROR_MSG: {HashMap data = (HashMap) msg.obj;boolean showBackground = Settings.Secure.getInt(mContext.getContentResolver(),Settings.Secure.ANR_SHOW_BACKGROUND, 0) != 0;synchronized (ActivityManagerService.this) {ProcessRecord proc = (ProcessRecord)data.get("app");AppErrorResult res = (AppErrorResult) data.get("result");if (proc != null && proc.crashDialog != null) {Slog.e(TAG, "App already has crash dialog: " + proc);if (res != null) {res.set(0);}return;}boolean isBackground = (UserHandle.getAppId(proc.uid)>= Process.FIRST_APPLICATION_UID&& proc.pid != MY_PID);for (int userId : mCurrentProfileIds) {isBackground &= (proc.userId != userId);}if (isBackground && !showBackground) {Slog.w(TAG, "Skipping crash dialog of " + proc + ": background");if (res != null) {res.set(0);}return;}if (mShowDialogs && !mSleeping && !mShuttingDown) {//创建crash提示框,等待用户选择,等待时间为5分钟Dialog d = new AppErrorDialog(mContext,ActivityManagerService.this, res, proc);d.show();proc.crashDialog = d;} else {//当处于sleep状态,则默认选择退出if (res != null) {res.set(0);}}}ensureBootCompleted();} break;...省略...}
}

所以在发生crash时,系统会默认弹出crash提示框,并阻塞等待用户选择,当用户不做任何选择时,5min后,默认选择退出。

最后调用上面1.2中注释4的逻辑,用来杀掉Crash进程。

到此Crash处理流程就跟踪完毕!!!

2. Android Crash流程总结

当进程抛出未捕获的异常时,系统就会处理该异常并进入crash处理流程。

2.1 Crash处理流程图

在这里插入图片描述

在流程图中,最重要的是AMS.handleAppCrashLocked中的功能,里面分了如下两种情况进行处理:

  1. 当同一个进程在1分钟内连续两次crash,则执行如下逻辑

    • 非persistent进程

      • 执行ASS.handleAppCrashLock方法,结束该应用所有activity

      • 执行AMS.removeProcessLocked方法,杀死该进程和同一个进程组下的所有进程

      • 执行ASS.resumeTopActivitiesLocked方法,恢复栈顶第一个没有finishing状态的activity

    • persistent进程

      • 执行ASS.resumeTopActivitiesLocked方法,恢复栈顶第一个没有finishing状态的activity
  2. 如果没有连续crash,则执行如下逻辑

    • 执行ASS.finishTopRunningActivityLocked方法,结束栈顶正在运行activity
2.2 Crash处理流程函数调用链
AMP.handleApplicationCrashAMS.handleApplicationCrashAMS.findAppProcessAMS.handleApplicationCrashInnerAMS.addErrorToDropBoxAMS.crashApplicationAMS.makeAppCrashingLockedAMS.startAppProblemLockedProcessRecord.stopFreezingAllLockedActivityRecord.stopFreezingScreenLockedWMS.stopFreezingScreenLockedWMS.stopFreezingDisplayLockedAMS.handleAppCrashLockedASS.handleAppCrashLockedAS.handleAppCrashLockedAS.finishCurrentActivityLockedAMS.removeProcessLockedProcessRecord.killAMS.handleAppDiedLockedASS.handleAppDiedLockedAMS.cleanUpApplicationRecordLockedAS.handleAppDiedLockedAS.removeHistoryRecordsForAppLockedASS.resumeTopActivitiesLockedAS.resumeTopActivityLockedAS.resumeTopActivityInnerLockedASS.finishTopRunningActivityLockedAS.finishTopRunningActivityLockedAS.finishActivityLockedmUiHandler.sendMessage(SHOW_ERROR_MSG)Process.killProcess(Process.myPid());
System.exit(10);

其中缩写对应关系如下:

AMP--->ActivityManagerProxyAMS--->ActivityManagerServiceWMS--->WindowManagerServiceASS--->ActivityStackSupervisorAS--->ActivityStack
非常感谢您的耐心阅读,希望我的文章对您有帮助。欢迎点评、转发或分享给您的朋友或技术群。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部