Android Crash处理流程分析

作者:神秘网友 发布时间:2020-09-09 02:21:52

Android Crash处理流程分析

Android Crash处理流程分析

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()完成的,用下面的流程图表示:

Android Crash处理流程分析

第4步是不准确的,Zygote起来后,ZygoteInit就把runSelectLoop起来了,一直在等着有人通过socket连接它。第7步中,已经fork了App进程,从这后面的代码就是运行在App的进程中了,下面重点看下commonInit函数的处理流程。

Force close的处理流程

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的处理中,会将发生异常的进程干掉。

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继续后续的处理
    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处理流程分析相关教程

  1. 一个bug的处理过程总结
  2. redis(五) 高级应用(事务处理持久化操作pub_sub虚拟内存)
  3. 秒懂Android注解处理器(Android Annotation Processor)
  4. Android开发工具Android Studio的使用
  5. Android开发中SQLite存储时间和按日期函数查询
  6. Android云通信IM系列(2)-基本问题
  7. Spring-Boot静态文件处理
  8. Android从零开搞系列:自定义View(13)新消息小圆点效果