深入理解 Android 9.0 Crash 机制 读懂底层原理 看清表面逻辑( 三 )

三、handleApplicationCrash处理分析handleApplicationCrash 会通过 JNI接口调用AMS中的方法 。
//发送 Crash 弹窗handler , 直到Dialog dismiss ActivityManager.getService().handleApplicationCrash( mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));ActivityManagerService.java
handleApplicationCrash 通过JNI 回调用 AMS中的handleApplicationCrash方法 , 进而调用AMS 中的内部方法handleApplicationCrashInner 。
handleApplicationCrash

  • 1.当远程IBinder对象为空Null时 , 则进程名为system_server;
  • 2.当远程IBinder对象不为空 , 且ProcessRecord为空时 , 则进程名为unknown;
  • 3.当远程IBinder对象不为空 , 且ProcessRecord不为空时 , 则进程名为ProcessRecord对象中相应进程名 。
// 当app Crash 时候 , 会调用此方法 。//调用结束后  , app 进程就会推出 public void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo) { // findAppProcess 详见 3.1 分析 ProcessRecord r = findAppProcess(app, "Crash"); // system_server 进程 为Nullfinal String processName = app == null ? "system_server" : (r == null ? "unknown" : r.processName); //handleApplicationCrashInner 详见 4 分析 handleApplicationCrashInner("crash", r, processName, crashInfo); }handleApplicationCrashInner主要是调用 AppErrors类中的crashApplication 方法处理 。
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName, ApplicationErrorReport.CrashInfo crashInfo) { ... ...//调用APP Error 类方法中的 crashApplication mAppErrors.crashApplication(r, crashInfo); }3.1 findAppProcess
ActivityManagerService.java
findAppProcess主要是通过for循环遍历查找出IBinder对应的Process.
private ProcessRecord findAppProcess(IBinder app, String reason) { ... ... synchronized (this) { final int NP = mProcessNames.getMap().size(); for (int ip=0; ip apps = mProcessNames.getMap().valueAt(ip); final int NA = apps.size(); for (int ia=0; ia ProcessRecord p = apps.valueAt(ia); //当找到目标进程则返回 if (p.thread != null && p.thread.asBinder() == app) { return p; } } } //如果代码执行到这里 , 表明无法找到应用所在的进程 return null; } }其中 mProcessNames = new ProcessMap();对于代码mProcessNames.getMap()返回的是mMap , 而mMap= new ArrayMap>();
知识延伸:SparseArray和ArrayMap是Android专门针对内存优化而设计的取代Java API中的HashMap的数据结构 。
对于key是int类型则使用SparseArray , 可避免自动装箱过程;
对于key为其他类型则使用ArrayMap 。
HashMap的查找和插入时间复杂度为O(1)的代价是牺牲大量的内存来实现的 , 而SparseArray和ArrayMap性能略逊于HashMap , 但更节省内存 。
再回到mMap , 这是以进程name为key , 再以(uid为key , 以ProcessRecord为Value的)结构体作为value 。下面看看其get()和put()方法
public E get(String name, int uid) { SparseArray uids = mMap.get(name); if (uids == null) return null; return uids.get(uid); }public E put(String name, int uid, E value) { SparseArray uids = mMap.get(name); if (uids == null) { uids = new SparseArray(2); mMap.put(name, uids); } uids.put(uid, value); return value; } findAppProcess()根据app(IBinder类型)来查询相应的目标对象ProcessRecord 。
有了进程记录对象ProcessRecord和进程名processName , 则进入执行Crash处理方法 AppErrors.java , 继续往下看 。
四、handleApplicationCrashInner 处理分析ActivityManagerService.java
void handleApplicationCrashInner(String eventType, ProcessRecord r, String processName, ApplicationErrorReport.CrashInfo crashInfo) { ... ... //将错误信息追加到DropBox addErrorToDropBox(eventType, r, processName, null, null, null, null, null, crashInfo); //【见小节5】 mAppErrors.crashApplication(r, crashInfo); }其中addErrorToDropBox是将Crash的信息输出到目录/data/system/dropbox 。例如system_server的dropbox文件名为system_server_crash@xxx.txt (xxx代表的是时间戳)
五、APP Error info分析AppErrors.java
AppErrors 主要是 控制APP Crash的场景条件 。
void crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) { ... ... try { // 调用内部 crashApplicationInner方法 crashApplicationInner(r, crashInfo, callingPid, callingUid); } finally { Binder.restoreCallingIdentity(origId); } }crashApplicationInner内部方法
void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo, int callingPid, int callingUid) { ... ...AppErrorResult result = new AppErrorResult(); TaskRecord task; synchronized (mService) { // 如果是通过IActivityController 实例导致的Crash  , 则不显示弹窗 // 详见5.1if (handleAppCrashInActivityController(r, crashInfo, shortMsg, longMsg, stackTrace, timeMillis, callingPid, callingUid)) { return; } ... ... AppErrorDialog.Data data = https://www.isolves.com/it/cxkf/ydd/Android/2019-11-13/new AppErrorDialog.Data(); data.result = result; data.proc = r; // 无法势必的进程 也不显示Crash 弹窗【见小节6】 if (r == null || !makeAppCrashingLocked(r, shortMsg, longMsg, stackTrace, data)) { return; } final Message msg = Message.obtain(); msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG; task = data.task; msg.obj = data; //发送消息SHOW_ERROR_MSG , 弹出提示crash的对话框 , 等待用户选择【见小节10】 mService.mUiHandler.sendMessage(msg); } //进入阻塞等待 , 直到用户选择crash对话框"退出"或者"退出并报告" int res = result.get(); Intent appErrorIntent = null; MetricsLogger.action(mContext, MetricsProto.MetricsEvent.ACTION_APP_CRASH, res); if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) { res = AppErrorDialog.FORCE_QUIT; } ... ...}


推荐阅读