Android Framework AMS(17)APP 异常Crash处理流程解读

该系列文章总纲链接:专题总纲目录 Android Framework 总纲


本章关键点总结 & 说明:

说明:本章节主要解读APP Crash处理。关注思维导图中左上侧部分即可。

本章节主要是对Android的APP Crash处理有一个基本的了解。从进程启动到UncaughtHandler处理方法的注册到UncaughtHandler方法异常处理、AMS的binderDied讣告流程分析。以便于我们更好地理解APP 异常处理的闭环流程。

1 从进程启动到UncaughtHandler处理方法的注册

我们需要对开机启动一个进程有一个了解,相关参考文章如下:

android 开机启动流程分析(11)Zygote启动分析

android 开机启动流程分析(13)Zygote的分裂

基于以上文章的内容解读,在Zygote分裂时,最终会执行到runOnce方法,关键代码如下:

//ZygoteConnection
    boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
        // ...
        //fork进程操作
        pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
                    parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
                    parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
                    parsedArgs.appDataDir);
        //...
        try {
            if (pid == 0) {
                // 在子进程中执行
                IoUtils.closeQuietly(serverPipeFd); // 关闭服务器端文件描述符
                serverPipeFd = null; // 清除文件描述符引用
                handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr); // 处理子进程

                // 这里不应该到达,因为子进程应该要么抛出ZygoteInit.MethodAndArgsCaller异常,要么执行exec()。
                return true;
            } else {
                // 在父进程中执行...pid小于0表示失败
                IoUtils.closeQuietly(childPipeFd); // 关闭客户端文件描述符
                childPipeFd = null; // 清除文件描述符引用
                return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs); // 处理父进程
            }
        } finally {
            IoUtils.closeQuietly(childPipeFd); // 最终关闭客户端文件描述符
            IoUtils.closeQuietly(serverPipeFd); // 最终关闭服务器端文件描述符
        }
    }

在fork进程后,fokr出来的子进程会调用handleChildProc方法,我们主要关注该方法,代码实现如下:

//ZygoteConnection
    private void handleChildProc(Arguments parsedArgs,
        FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
        throws ZygoteInit.MethodAndArgsCaller {

        // 关闭socket连接
        closeSocket();
        // 关闭Zygote服务器的socket
        ZygoteInit.closeServerSocket();

        // 如果提供了文件描述符数组
        if (descriptors != null) {
            try {
                // 重定向标准输入、输出和错误流
                ZygoteInit.reopenStdio(descriptors[0], descriptors[1], descriptors[2]);

                // 关闭文件描述符数组中的所有文件描述符
                for (FileDescriptor fd : descriptors) {
                    IoUtils.closeQuietly(fd);
                }
                // 设置新的标准错误流
                newStderr = System.err;
            } catch (IOException ex) {
                // 记录重定向标准IO时的错误
                Log.e(TAG, "Error reopening stdio", ex);
            }
        }

        // 如果提供了nice名称(进程名),设置进程名
        if (parsedArgs.niceName != null) {
            Process.setArgV0(parsedArgs.niceName);
        }

        // 如果需要运行时初始化
        if (parsedArgs.runtimeInit) {
            // 如果提供了invokeWith(启动命令),使用WrapperInit启动应用程序
            if (parsedArgs.invokeWith != null) {
                WrapperInit.execApplication(parsedArgs.invokeWith,
                        parsedArgs.niceName, parsedArgs.targetSdkVersion,
                        pipeFd, parsedArgs.remainingArgs);
            } else {
                // 否则,使用RuntimeInit启动应用程序
                RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
                        parsedArgs.remainingArgs, null /* classLoader */);
            }
        } else {
            // 如果不需要运行时初始化,直接启动指定的类和主方法
            String className;
            try {
                // 获取类名
                className = parsedArgs.remainingArgs[0];
            } catch (ArrayIndexOutOfBoundsException ex) {
                // 如果类名参数缺失,记录错误并退出
                logAndPrintError(newStderr,
                        "Missing required class name argument", null);
                return;
            }

            // 准备主方法的参数
            String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
            System.arraycopy(parsedArgs.remainingArgs, 1,
                    mainArgs, 0, mainArgs.length);

            // 如果提供了invokeWith(启动命令),使用WrapperInit启动独立应用程序
            if (parsedArgs.invokeWith != null) {
                WrapperInit.execStandalone(parsedArgs.invokeWith,
                        parsedArgs.classpath, className, mainArgs);
            } else {
                // 否则,使用ClassLoader加载类并执行主方法
                ClassLoader cloader;
                if (parsedArgs.classpath != null) {
                    cloader = new PathClassLoader(parsedArgs.classpath,
                            ClassLoader.getSystemClassLoader());
                } else {
                    cloader = ClassLoader.getSystemClassLoader();
                }

                try {
                    ZygoteInit.invokeStaticMain(cloader, className, mainArgs);
                } catch (RuntimeException ex) {
                    // 如果启动过程中出现异常,记录错误
                    logAndPrintError(newStderr, "Error starting.", ex);
                }
            }
        }
    }

这里如果是第一次启动APP进程,那么parsedArgs.runtimeInit的值为true,invokeWith为null,这时候会调用到RuntimeInit.zygoteInit方法,该方法实现如下:

//RuntimeInit
    public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
            throws ZygoteInit.MethodAndArgsCaller {
        if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
        
        // 重定向日志流,使得应用程序的日志能够被正确地输出
        redirectLogStreams();
        
        // 执行公共初始化操作,这部分代码涉及到设置进程的基本信息,如进程名称等
        commonInit();
        
        // 调用本地方法进行Zygote初始化,这可能涉及到JNI调用,设置本地环境等
        nativeZygoteInit();

        // 执行应用程序特定的初始化操作,这部分代码会根据传入的参数和类加载器加载应用程序的类,并启动应用程序的主线程
        applicationInit(targetSdkVersion, argv, classLoader);
    }

这里因为我们主要关注未处理的异常处理,这里的commonInit关键代码实现如下:

//RuntimeInit
    private static final void commonInit() {
        Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
        //...
        initialized = true;
    }

这里当APP抛出未处理的异常时,都是由UncaughtHandler来处理的。到此才是APP异常处理的逻辑。

注意:如果应用程序在Android中自行设定了UncaughtExceptionHandler,这将优先于系统默认的异常处理机制。因此,当应用程序发生未捕获异常时,系统框架的崩溃处理流程将不会被触发,而是执行自定义的异常处理逻辑。通过这种方式,可以实现错误日志的自动上报功能,并且在捕获异常时防止应用程序崩溃,避免显示崩溃对话框。简而言之,自定义的UncaughtExceptionHandler允许APP开发者接管异常处理,进行日志记录和稳定性维护,而不采用系统的默认崩溃响应。

2 UncaughtHandler方法异常处理解读

UncaughtHandler的代码实现如下:

//RuntimeInit
    private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
        // 处理未捕获异常的方法
        public void uncaughtException(Thread t, Throwable e) {
            try {
                // 避免重复进入——如果崩溃报告本身崩溃,避免无限循环。
                if (mCrashing) return;
                mCrashing = true;

                // 如果mApplicationObject为空,表示系统进程发生了致命异常
                if (mApplicationObject == null) {
                    Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图王大胜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值