Android crash 默认处理流程

来源:互联网 发布:剑雨江湖进阶数据仙羽 编辑:程序博客网 时间:2024/05/19 14:00

Android APP crash 的时候会弹出一个 Force close Dialog,这篇文章主要记录一下该 Dialog 的显示流程,以及由此引申出来的自定义 APP crash 处理流程

  1. Force close Dialog 的显示流程
  2. 自定义 Thread.UncaughtExceptionHandler 处理 APP crash

Force close 处理流程

Android 主要使用Thread.UncaughtExceptionHandler 机制来处理 APP Crash,这个defaultUncaughtException 定义在 RuntimeInit.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 {            ......            if (mApplicationObject == null) {                Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);            } else {                StringBuilder message = new StringBuilder();                // 1) 把Exception信息打印到logcat中                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);            }            ......            // 2) 显示 ForceClose Dialog            ActivityManagerNative.getDefault().handleApplicationCrash(                    mApplicationObject, new ApplicationErrorReport.CrashInfo(e));        } catch (Throwable t2) {            ......        } finally {            // Try everything to make sure this process goes away.            Process.killProcess(Process.myPid());            System.exit(10);        }    }}

可以看到,这个默认的Thread.UncaughtExceptionHandler主要做了两件事

  1. 打印出Exception信息,这里TAG为AndroidRuntime,主要信息包含:线程名,进程名,进程ID,以及具体的Exception
    Force close Dialog
  2. 调用AMS.handleApplicationCrash 显示 Force close dialog

我们进入 AMS.handleApplicationCrash

public void handleApplicationCrash(IBinder app, ApplicationErrorReport.CrashInfo crashInfo) {    ......    // 直接调用 handleApplicationCrashInner    handleApplicationCrashInner("crash", r, processName, crashInfo);}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);}

handleApplicationCrashInner 会首先把 crash 信息写入 EventLog 和 DropBox 中,关于这两个系统还不是很清楚,后续有机会再补上,在网上找到下面这一段描述:

  1. EventLog保存在/system/etc/event-log-tag中。
  2. Dropbox的log保存在/data/system/dropbox目录下,它的文件名以system_server或system_app或data_app打头,然后以_crash开始。

接着调用AppErrors.crashApplication

// AppErrors.javavoid crashApplication(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {    final long origId = Binder.clearCallingIdentity();    try {        // 直接进入crashApplicationInner        crashApplicationInner(r, crashInfo);    } finally {        Binder.restoreCallingIdentity(origId);    }}void crashApplicationInner(ProcessRecord r, ApplicationErrorReport.CrashInfo crashInfo) {    ......    AppErrorResult result = new AppErrorResult();    TaskRecord task;    synchronized (mService) {        ......        AppErrorDialog.Data data = new AppErrorDialog.Data();        data.result = result;        data.proc = r;        ......        1)使用Handler发送显示Dialog消息        Message msg = Message.obtain();        msg.what = ActivityManagerService.SHOW_ERROR_UI_MSG;        task = data.task;        msg.obj = data;        mService.mUiHandler.sendMessage(msg);    }    2)等待用户对Dialog的处理结果    int res = result.get();    ......    if (res == AppErrorDialog.TIMEOUT || res == AppErrorDialog.CANCEL) {        res = AppErrorDialog.FORCE_QUIT;    }    // 根据用户的选择结果做不同的处理    synchronized (mService) {        if (res == AppErrorDialog.MUTE) {            ......        }        if (res == AppErrorDialog.RESTART) {            ......        }        if (res == AppErrorDialog.FORCE_QUIT) {            ......        }        if (res == AppErrorDialog.FORCE_QUIT_AND_REPORT) {            ......        }        ......    }    ......}

这里先使用Handler发送一个显示Dialog的消息,再等待用户的选择结果

我们先看步骤2,这里其实只是简单的wait()

final class AppErrorResult {    public void set(int res) {        synchronized (this) {            mHasResult = true;            mResult = res;            notifyAll();        }    }    public int get() {        synchronized (this) {            while (!mHasResult) {                try {                    wait();                } catch (InterruptedException e) {                }            }        }        return mResult;    }    boolean mHasResult = false;    int mResult;}

我们看到这里 result.get() 会一直等待直到 result.set() 被调用,也就是用户点击了对话框里的Button或者超时(5分钟)Dialog自动dismiss。

再看步骤1

// ActivityManagerService.javafinal class UiHandler extends Handler {      @Override    public void handleMessage(Message msg) {        switch (msg.what) {            case SHOW_ERROR_UI_MSG: {                mAppErrors.handleShowAppErrorUi(msg);                ensureBootCompleted();            } break;        ......        }    }}// AppErrors.javavoid handleShowAppErrorUi(Message msg) {    AppErrorDialog.Data data = (AppErrorDialog.Data) msg.obj;    synchronized (mService) {        ProcessRecord proc = data.proc;        ......        if (mService.canShowErrorDialogs() && !crashSilenced) {            // AppErrorDialog 是一个 AlertDialog            proc.crashDialog = new AppErrorDialog(mContext, mService, data);        } else {            ......        }    }    // 显示Dialog    if(data.proc.crashDialog != null) {        data.proc.crashDialog.show();    }}

至此CrashDialog就显示出来了。

自定义 UncaughtExceptionHandler

系统默认弹出Force close Dialog 的方式对用户其实不是很友好,我们可以自定义一个UncaughtExceptionHandler,然后调用 Thread.setDefaultUncaughtExceptionHandler 将其设置为默认处理器(通常是在应用的Application.onCreate() 里设置),具体的可以参考 捕获全局异常UncaughtExceptionHandler

原创粉丝点击