欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android Crash处理流程分析

程序员文章站 2022-04-09 10:30:02
...

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的处理流程

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是否设置了,如果设置了会重启该应用。

相关标签: Crash