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);    }
  1. 将crash信息写入到EventLog中,eventlog保存在/system/etc/event-log-tag中。
  2. 将log记录到DropBox中,dropbox的log保存在/data/system/dropbox目录下,它的文件名以system_server或system_app或data_app打头,然后以_crash开始。
  3. 调用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是否设置了,如果设置了会重启该应用。

0 0