Android Crash处理流程分析
来源:互联网 发布:php exec 返回1 编辑:程序博客网 时间:2024/05/27 14:13
Android的crash主要有3种,java层的force close,native层的crash和ANR。检查这三种crash的log方法也不相同:分别搜索“FATAL EXCEPTION”, “fault addr”和”ANR”。这三种crash的处理流程有不同,也有很多共性,但不管哪种crash,都先要导致了解下App的启动流程。
App的启动流程
App启动时,如果不和其他应用shareUserId,将会为自己创建一个进程空间,是通过调用ActivityManagerService的startProcessLocked()完成的,而Android的crash主要有3种,java层的force close,native层的crash和ANR。检查这三种crash的log方法也不相同:分别搜索“FATAL EXCEPTION”, “fault addr”和”ANR”。这三种crash的处理流程有不同,也有很多共性,但不管哪种crash,都先要导致了解下App的启动流程。
App的启动流程
App启动时,如果不和其他应用shareUserId,将会为自己创建一个进程空间,是通过调用ActivityManagerService的startProcessLocked()完成的,用下面的流程图表示:
第4步是不准确的,Zygote起来后,ZygoteInit就把runSelectLoop起来了,一直在等着有人通过socket连接它。第7步中,已经fork了App进程,从这后面的代码就是运行在App的进程中了,下面重点看下commonInit函数的处理流程。
Force close的处理流程
UncaughtHandler
App之所以出现了force close,就是有异常抛出,而App应用没有catch到。上节说到commonInit,这儿看下commonInit的代码:
private static final void commonInit() { ...... /* set default handler; this applies to all threads in the VM */ Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler()); ...... }
设置了一个默认的异常处理器。
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler { public void uncaughtException(Thread t, Throwable e) { try { // Don't re-enter -- avoid infinite loops if crash-reporting crashes. if (mCrashing) return; mCrashing = true; if (mApplicationObject == null) { Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e); } else { StringBuilder 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); } ...... // Bring up crash dialog, wait for it to be dismissed ActivityManagerNative.getDefault().handleApplicationCrash( mApplicationObject, new ApplicationErrorReport.CrashInfo(e)); } catch (Throwable t2) { if (t2 instanceof DeadObjectException) { // System process is dead; ignore } else { try { Clog_e(TAG, "Error reporting crash", t2); } catch (Throwable t3) { // Even Clog_e() fails! Oh well. } } } finally { // Try everything to make sure this process goes away. Process.killProcess(Process.myPid()); System.exit(10); } } }
在这里面会打出异常栈中的信息,之所以force close搜索时使用“FATAL EXCEPTION”也是在这儿体现的。
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e))从注释上看是要调用crash的对话框,过会看下它,在finally的处理中,会将发生异常的进程干掉。
handleApplicationCrash的处理流程
ActivityManagerNative.getDefault()返回的ActivityManagerProxy的实例,每一个应用进程中都有一个唯一的ActivityManagerProxy实例,它通过Binder发送命令“HANDLE_APPLICATION_CRASH_TRANSACTION”给ActivityManagerNative(实际上就是ActivityManagerService)的Server端,然后ActivityManagerService的handleApplicationCrash函数就会被调用。
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) { ProcessRecord r = findAppProcess(app, "Crash"); final String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); handleApplicationCrashInner("crash", r, processName, crashInfo); }
这里面有两个参数,第一个参数,就是UncaughtHandler实例传进来的mApplicationObject,第二个参数是根据Throwable构建的一个ApplicationErrorReport.CrashInfo实例。第二个参数很容易懂,那么第一个参数代表啥意思呢?
第一节中讲应用启动时只说了一个片段,剩下的还要好多步骤。应用的进程创建了,只是创建了一个运行环境而已。在AMS调用Process.start的第一个参数是“android.app.ActivityThread”,在第11步中applicationInit会调用其main方法,main中会实例化一个ActivityThread,所以很多人说ActivityThread代表着App的主线程。ActivityThread中的attach会被调用,在该方法中会调用RuntimeInit.setApplicationObject(mAppThread.asBinder()),此时mAppThread.asBinder())的值被赋给了mApplicationObject,实际上它就是ApplicationThread,而ApplicationThread是作为App的Binder存在的,从某种意义上代表着ActivityThread。
public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) { ProcessRecord r = findAppProcess(app, "Crash"); final String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); handleApplicationCrashInner("crash", r, processName, crashInfo); }
ProcessRecord 代表着该应用的进程信息,在想zygote要求fork进程钱会创建它。
findAppProcess会根据ApplicationThread查找到对应的应用进程的记录。ApplicationThread的值是在完成应用进程的创建后,调用AMS的attachApplication是穿过来的,attachApplication会根据fork进程得到的应用的pid值,查找到ProcessRecord的实例,然后它的makeActive方法设置的。
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName, ApplicationErrorReport.CrashInfo crashInfo) { EventLog.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); addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo); mAppErrors.crashApplication(r, crashInfo); }
- 将crash信息写入到EventLog中,eventlog保存在/system/etc/event-log-tag中。
- 将log记录到DropBox中,dropbox的log保存在/data/system/dropbox目录下,它的文件名以system_server或system_app或data_app打头,然后以_crash开始。
- 调用crashApplication继续后续的处理
crashApplication
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { final long origId = Binder.clearCallingIdentity(); try { crashApplicationInner(r, crashInfo); } finally { Binder.restoreCallingIdentity(origId); } }
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { ...... synchronized (mService) { /** * If crash is handled by instance of {@link android.app.IActivityController}, * finish now and don't show the app error dialog. */ // 处理有Controller的情况,Controller是一个测试的接口 if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace, timeMillis)) { return; } /** * If this process was running instrumentation, finish now - it will be handled in * {@link ActivityManagerService#handleAppDiedLocked}. */ if (r != null && r.instrumentationClass != null) { return; } ...... // 弹出crash的对话框 AppErrorDialog.Data data = new AppErrorDialog.Data(); ... mService.mUiHandler.sendMessage(msg); } // 等待用户选择对话框的结果 int res = result.get(); // 根据用户的选择处理不同的case Intent appErrorIntent = null; MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res); if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) { res = AppErrorDialog.FORCE_QUIT; } synchronized (mService) { if (res == AppErrorDialog.MUTE) { stopReportingCrashesLocked(r); } if (res == AppErrorDialog.RESTART) { mService.removeProcessLocked(r, false, true, "crash"); if (task != null) { try { mService.startActivityFromRecents(task.taskId, ActivityOptions.makeBasic().toBundle()); } catch (IllegalArgumentException e) { // Hmm, that didn't work, app might have crashed before creating a // recents entry. Let's see if we have a safe-to-restart intent. final Set<String> cats = task.intent.getCategories(); if (cats != null && cats.contains(Intent.CATEGORY_LAUNCHER)) { mService.startActivityInPackage(task.mCallingUid, task.mCallingPackage, task.intent, null, null, null, 0, 0, ActivityOptions.makeBasic().toBundle(), task.userId, null, null); } } } } if (res == AppErrorDialog.FORCE_QUIT) { long orig = Binder.clearCallingIdentity(); try { // Kill it with fire! mService.mStackSupervisor.handleAppCrashLocked(r); if (!r.persistent) { mService.removeProcessLocked(r, false, false, "crash"); mService.mStackSupervisor.resumeFocusedStackTopActivityLocked(); } } finally { Binder.restoreCallingIdentity(orig); } } if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) { appErrorIntent = createAppErrorIntentLocked(r, timeMillis, crashInfo); } if (r != null && !r.isolated && res != AppErrorDialog.RESTART) { // XXX Can't keep track of crash time for isolated processes, // since they don't have a persistent identity. mProcessCrashTimes.put(r.info.processName, r.uid, SystemClock.uptimeMillis()); } } if (appErrorIntent != null) { try { mContext.startActivityAsUser(appErrorIntent, new UserHandle(r.userId)); } catch (ActivityNotFoundException e) { Slog.w(TAG, "bug report receiver dissappeared", e); } } }
这些主要是一些清理工作,另外如果在弹出的对话框中,选择上报,会发送Intent.ACTION_APP_ERROR启动一个上报的Activity。
这一大坨代码主要的作用就是做一些善后工作,并将应用进程杀死。
进程死后的善后
在应用进程fork后的attach步骤中,有类似如下的代码
public final void attachApplication(IApplicationThread thread) { synchronized (this) { int callingPid = Binder.getCallingPid(); final long origId = Binder.clearCallingIdentity(); attachApplicationLocked(thread, callingPid); Binder.restoreCallingIdentity(origId); } }private final boolean attachApplicationLocked(IApplicationThread thread, ...... try { AppDeathRecipient adr = new AppDeathRecipient( app, pid, thread); thread.asBinder().linkToDeath(adr, 0); app.deathRecipient = adr; } catch (RemoteException e) { app.resetPackageList(mProcessStats); startProcessLocked(app, "link fail", processName); return false; } ......}
thread就是ApplicationThread类型的实例,这段代码向ApplicationThread注册了一个死亡通知,当app的进程被杀死后,AppDeathRecipient的binderDied函数将会被调用。
public void binderDied() { if (DEBUG_ALL) Slog.v( TAG, "Death received in " + this + " for thread " + mAppThread.asBinder()); synchronized(ActivityManagerService.this) { appDiedLocked(mApp, mPid, mAppThread, true); } }
appDiedLocked做的工作主要是清理。在cleanUpApplicationRecordLocked函数中还会检查App的android:persistent是否设置了,如果设置了会重启该应用。
- Android Crash处理流程分析
- Android crash 默认处理流程
- 理解Android Crash处理流程 (Android M 6.0)
- android app 默认的crash处理流程 (FATAL EXCEPTION)
- ActivityManagerService native crash处理流程
- 理解Native Crash处理流程
- android TouchEvent处理流程分析
- Android事件处理流程分析
- Android事件处理流程分析
- Android程序crash处理
- Android--应用Crash处理
- Android Crash异常处理
- android crash处理
- Android Crash抓取处理
- Android Crash抓取处理
- Android 应用Crash处理
- Android中crash处理
- android crash 分析
- CentOs7用phpize安装mcrypt扩展时遇到错误
- ACboy needs your help again! (栈、队列 模拟)
- 一、我的Python之路 -- 初识
- 阻止445端口连接
- acm杭电HDU2177取(2堆)石子游戏(威佐夫博弈)
- Android Crash处理流程分析
- 网页抓取手机号
- spring,freemarker,异步发邮件
- 在MyEclipse中启动项目正常,但不能访问Tomcat默认主页解决办法
- powershell使用心得
- Spring Boot (一)
- 一站式学习Wireshark(二):应用Wireshark观察基本网络协议
- solr6.2从环境部署到与mysql整合到中文分词器到solrJ的使用
- js 字符串的一些常用方法