Android Crash优化

2022-10-31/2022-10-31

异常捕获分析

Java 异常捕获

如果java层抛出了异常并且没有捕获的话,JVM将调用Thread类中的dispatchUncaughtException

/**
     * Dispatch an uncaught exception to the handler. This method is
     * intended to be called only by the runtime and by tests.
     *
     * @hide
     */
    // Android-changed: Make dispatchUncaughtException() public, for use by tests.
    public final void dispatchUncaughtException(Throwable e) {
        // BEGIN Android-added: uncaughtExceptionPreHandler for use by platform.
        Thread.UncaughtExceptionHandler initialUeh =
                Thread.getUncaughtExceptionPreHandler();
        if (initialUeh != null) {
            try {
                initialUeh.uncaughtException(this, e);
            } catch (RuntimeException | Error ignored) {
                // Throwables thrown by the initial handler are ignored
            }
        }
        // END Android-added: uncaughtExceptionPreHandler for use by platform.
        getUncaughtExceptionHandler().uncaughtException(this, e);
    }

    /**
     * Returns the handler invoked when this thread abruptly terminates
     * due to an uncaught exception. If this thread has not had an
     * uncaught exception handler explicitly set then this thread's
     * <tt>ThreadGroup</tt> object is returned, unless this thread
     * has terminated, in which case <tt>null</tt> is returned.
     * @since 1.5
     * @return the uncaught exception handler for this thread
     */
    public UncaughtExceptionHandler getUncaughtExceptionHandler() {
        return uncaughtExceptionHandler != null ?
            uncaughtExceptionHandler : group;
    }

可以看到这里的处理逻辑是如果存在PreHandler则将异常交给PreHandler处理,如果没有则再看Thread中有没有设置ExceptionHandler,如果设置了则交给这个Handler处理,如果没有设置则交给ThreadGroup去处理

/**
     * Called by the Java Virtual Machine when a thread in this
     * thread group stops because of an uncaught exception, and the thread
     * does not have a specific {@link Thread.UncaughtExceptionHandler}
     * installed.
     * <p>
     * The <code>uncaughtException</code> method of
     * <code>ThreadGroup</code> does the following:
     * <ul>
     * <li>If this thread group has a parent thread group, the
     *     <code>uncaughtException</code> method of that parent is called
     *     with the same two arguments.
     * <li>Otherwise, this method checks to see if there is a
     *     {@linkplain Thread#getDefaultUncaughtExceptionHandler default
     *     uncaught exception handler} installed, and if so, its
     *     <code>uncaughtException</code> method is called with the same
     *     two arguments.
     * <li>Otherwise, this method determines if the <code>Throwable</code>
     *     argument is an instance of {@link ThreadDeath}. If so, nothing
     *     special is done. Otherwise, a message containing the
     *     thread's name, as returned from the thread's {@link
     *     Thread#getName getName} method, and a stack backtrace,
     *     using the <code>Throwable</code>'s {@link
     *     Throwable#printStackTrace printStackTrace} method, is
     *     printed to the {@linkplain System#err standard error stream}.
     * </ul>
     * <p>
     * Applications can override this method in subclasses of
     * <code>ThreadGroup</code> to provide alternative handling of
     * uncaught exceptions.
     *
     * @param   t   the thread that is about to exit.
     * @param   e   the uncaught exception.
     * @since   JDK1.0
     */
    public void uncaughtException(Thread t, Throwable e) {
        if (parent != null) {
            parent.uncaughtException(t, e);
        } else {
            Thread.UncaughtExceptionHandler ueh =
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
                System.err.print("Exception in thread \""
                                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

ThreadGroup中的处理逻辑是

父线程组能处理则交给父线程组处理

否则尝试利用一个默认的defaultUncaughtExceptionHandler来处理异常,

而这个defaultUncaughtExceptionHandler是Thread类的一个静态变量,会在android系统启动时由RuntimeInit类初始化为KillApplicationHandler

public static final void main(String[] argv) {
        enableDdms();
        if (argv.length == 2 && argv[1].equals("application")) {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
            redirectLogStreams();
        } else {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
        }

        //在commonInit里
        commonInit();

        /*
         * Now that we're running in interpreted code, call back into native code
         * to run the system.
         */
        nativeFinishInit();

        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
    }

    @UnsupportedAppUsage
    protected static final void commonInit() {
        if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");

        /*
         * set handlers; these apply to all threads in the VM. Apps can replace
         * the default handler, but not the pre handler.
         */
        LoggingHandler loggingHandler = new LoggingHandler();
        RuntimeHooks.setUncaughtExceptionPreHandler(loggingHandler);
        Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));

        initialized = true;
    }

KillApplicationHandler对异常的处理方式是首先调用AMS提供的服务通过DropBox将crash信息存入磁盘,然后再杀掉app的进程

总结一下

如果java层抛出了异常并且没有被捕获的话,JVM将调用Thread类中的dispatchUncaughtException来处理异常,接下来会看Thread类是否有设置异常处理的handler来处理这个异常,如果没有则交给ThreadGroup来处理,ThreadGroup会先尝试交给父ThreadGroup来处理,如果无法处理则交给Thread类的一个静态变量defaultUncaughtExceptionHandler来处理,一般我们没有专门设置的话异常就会交给这个Handler来处理,这个defaultUncaughtExceptionHandler会在android系统启动时在RuntimeInit类初始化时初始化为KillApplicationHandler的实例,KillApplicationHandler对异常的处理方式为首先调用AMS提供的服务通过DropBox将crash信息存入磁盘,然后再杀掉app的进程

Java Exception捕获库

地址:https://github.com/OkAndGreat/CrashDecter

使用

image-20221031210836942

默认提供了四种发生异常时的处理策略:

image-20221031210928041

从上到下依次为重启app,返回到MainActivity,啥也不做和杀死当前抛出异常的Activity

当然也可以实现ICrashStrategy自己去自定义策略

CrashListener是当捕捉到异常后提供给使用者的回调接口

原理分析

从init开始分析

fun init(ctx: Application, strategy: ICrashStrategy, listener: CrashListener?) {
        LifecycleCallback.init(ctx)
        ActivityLifeCycleCrashHandler.install(ctx, listener)
        mContext = ctx
        crashListener = listener
        crashStrategy = strategy
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

逐行进行分析

object LifecycleCallback : Application.ActivityLifecycleCallbacks by noOpDelegate() {
    fun init(application: Application){
        application.registerActivityLifecycleCallbacks(this)
    }

    override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
        ActivityCrashManager.addActivity(activity)
    }

    override fun onActivityDestroyed(activity: Activity) {
        ActivityCrashManager.removeActivity(activity)
    }
}

LifecycleCallback是为了维护一个Activity的栈,以供发生异常时的处理策略使用:

object ActivityCrashManager {

    private val activityStack: Stack<Activity> by lazy {
        Stack<Activity>()
    }

    fun addActivity(activity: Activity) {
        activityStack.add(activity)
    }

    fun removeActivity(activity: Activity) {
        activityStack.remove(activity)
    }

    fun killTopActivity() {
        val a = activityStack.pop()
        a.finish()
    }

    fun backToMainActivity() {
        while (activityStack.size > 1) {
            val a = activityStack.pop()
            a.finish()
        }
    }
}

再看到ActivityLifeCycleCrashHandler

fun install(ctx: Context, listener: CrashListener?) {
        if (mInstalled) {
            return
        }
        try {
            Reflection.unseal(ctx)
        } catch (t: Throwable) {
            t.printStackTrace()
        }
        mInstalled = true
        mCrashListener = listener
        initActivityKiller()
    }

    private fun initActivityKiller() {
        if (Build.VERSION.SDK_INT >= 28) {
            mActivityKiller = ActivityKillerV28()
        } else if (Build.VERSION.SDK_INT >= 26) {
            mActivityKiller = ActivityKillerV26()
        } else if (Build.VERSION.SDK_INT == 25 || Build.VERSION.SDK_INT == 24) {
            mActivityKiller = ActivityKillerV24_V25()
        } else if (Build.VERSION.SDK_INT <= 23) {
            mActivityKiller = ActivityKillerV21_V23()
        }
        try {
            hookmH()
        } catch (e: Throwable) {
            e.printStackTrace()
        }
    }

主要是初始化了ActivityKiller,然后hook ActivityThread中的mH然后对Activity的生命周期进行异常监听,如果发生了异常则kill这个Activity,因为不同版本的AOSP源码不同导致了需要对杀死Activity的方法做调整因此有了好几个ActivityKiller

然后在对Thread的DefaultUncaughtExceptionHandler设置成了CrashHandler,通过上面的分析可知当发生异常时会走到CrashHandler的uncaughtException,实现为:

override fun uncaughtException(t: Thread, e: Throwable) {
        crashListener?.onExceptionCaught(e)
        crashStrategy.onExceptionHappened(mContext)
        ActivityLifeCycleCrashHandler.onExceptionCaught(t, e)
    }

就是回调了一些接口


标题:Android Crash优化
作者:OkAndGreat
地址:http://zhongtai521.wang/articles/2022/10/31/1667222469497.html

评论
发表评论
       
       
取消