JE crash处理流程

Jave Crash 处理流程

[基于 android P]

一、概述

App crash(全称Application crash), 对于Crash可分为native crash和java crash,对于crash相信很多app开发者都会遇到,那么上层什么时候会出现crash呢,系统又是如何处理crash的呢。例如,在app大家经常使用try…catch语句,那么如果没有有效catch exception,就是导致应用crash,系统便会来捕获,并进入crash流程。

我们知道上层应用进程都是由Zygote fork而来,分为system_server系统进程和各种应用进程,在这些进程创建之初会设置未捕获异常的处理器,当系统抛出未捕获的异常时,最终都交给异常处理器,即RuntimeInit.java的commonInit方法设置UncaughtHandler来捕获异常。

crash处理流程的堆栈调用关系:
RuntimeInit.setDefaultUncaughtExceptionHandler
	RuntimeInit.KillApplicationHandler.uncaughtException
		AM.getService().handleApplicationCrash(binder通信)
		    AMS.handleApplicationCrash
		        AMS.findAppProcess
		        AMS.handleApplicationCrashInner
		            AMS.addErrorToDropBox
		            AppErr.crashApplication
						AppErr.crashApplicationInner
		                	AMS.makeAppCrashingLocked
		                    	AMS.startAppProblemLocked
		                    	ProcessRecord.stopFreezingAllLocked
		                        	ActivityRecord.stopFreezingScreenLocked
		                                WMS.stopFreezingDisplayLocked
		                    	AMS.handleAppCrashLocked
		                	mUiHandler.sendMessage(SHOW_ERROR_UI_MSG)
		
		Process.killProcess(Process.myPid());
		System.exit(10);

二、Crash处理流程

那么接下来以commonInit()方法为起点来梳理流程。

1. RuntimeInit.commonInit
public class RuntimeInit {
    ...
    private static final void commonInit() {
        //设置默认的未捕获异常处理器,UncaughtHandler实例化过程
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler());
        ...
    }
}

setDefaultUncaughtExceptionHandler()只是将异常处理器handler对象赋给Thread成员变量,接下来看看KillApplicationHandler对象实例化过程。

2. UncaughtHandler

[–>RuntimeInit.java]

private static class KillApplicationHandler implements Thread.UncaughtExceptionHandler{
    //覆写接口方法
    public void uncaughtException(Thread t, Throwable e) {
		try {
            ensureLogging(t, e);//【2.1】
            ......
            // 【3】
            ActivityManager.getService().handleApplicationCrash(
                    mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
        } catch (Throwable t2) {
            ......
        } finally {
            // 确保进程被杀死【11】
            Process.killProcess(Process.myPid());
            System.exit(10);
        }
        
}
2.1 ensureLogging

[–> RuntimeInit.java]

private void ensureLogging(Thread t, Throwable e) {
    if (!mLoggingHandler.mTriggered) {
        try {
			//【2.2】
            mLoggingHandler.uncaughtException(t, e);
        } catch (Throwable loggingThrowable) {
            // Ignored.
        }
    }
}
2.2 uncaughtException

[-> RuntimeInit.LoggingHandler]

private static class LoggingHandler implements Thread.UncaughtExceptionHandler {
    public volatile boolean mTriggered = false;

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        mTriggered = true;
        if (mCrashing) return;
		//注意: mApplicationObject等于null,一定不是普通的app进程. 
		//除了system进程, 也有可能是shell进程, 即通过app_process + 命令参数 的方式创建的进程。
        if (mApplicationObject == null && (Process.SYSTEM_UID == Process.myUid())) {
            Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
        } else {
            StringBuilder message = new StringBuilder();
            // The "FATAL EXCEPTION" string is still used on Android even though
            // apps can set a custom UncaughtExceptionHandler that renders uncaught
            // exceptions non-fatal.
            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);
        }
    }
}

当system进程crash的信息:
开头*** FATAL EXCEPTION IN SYSTEM PROCESS [线程名];
接着输出发生crash时的调用栈信息;

当app进程crash时的信息:
开头FATAL EXCEPTION: [线程名];
紧接着 Process: [进程名], PID: [进程id];
最后输出发生crash时的调用栈信息,
app crash 保存在crash_log中。

当输出完crash信息到logcat里面,这只是crash流程的刚开始阶段,接下来弹出crash对话框,经过binder调用最终交给ActivityManagerService(简称AMS)中相应的方法去处理,故接下来调用的是AMS.handleApplicationCrash()。

3. handleApplicationCrash

[–>ActivityManagerService.java]

public void handleApplicationCrash(IBinder app, ApplicationErrorReport.ParcelableCrashInfo crashInfo) {
    //获取Processrecord对象 【3.1】
    ProcessRecord r = findAppProcess(app, "Crash");
    final String processName = app == null ? "system_server"
            : (r == null ? "unknown" : r.processName);
    //【4】
    handleApplicationCrashInner("crash", r, processName, crashInfo);
}

关于进程名(processName):

  • 当远程IBinder对象为空时,则进程名为system_server;
  • 当远程IBinder对象不为空,且ProcessRecord为空时,则进程名为unknown;
  • 当远程IBinder对象不为空,且ProcessRecord不为空时,则进程名为ProcessRecord对象中相应进程名。
3.1 findAppProcess

[–>ActivityManagerService.java]

private ProcessRecord findAppProcess(IBinder app, String reason) {
    if (app == null) {
        return null;
    }

    synchronized (this) {
        final int NP = mProcessNames.getMap().size();
        for (int ip=0; ip<NP; ip++) {
            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
            final int NA = apps.size();
            for (int ia=0; ia<NA; 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<String, SparseArray>();

知识延伸: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。

有了进程记录对象ProcessRecord和进程名processName,则进入执行Crash处理方法。

4. handleApplicationCrashInner

[–>Activit

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值