3

都2021年了,你的android app还在发生crash吗?

 3 years ago
source link: http://www.androidchina.net/11456.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

既然都来了,你知道为什么app会crash吗?

android main入口的commonInit()方法内处,有这么一句话,

Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));

如果没有这句话,app就不会crash。不信,你往里面看,

public KillApplicationHandler(LoggingHandler loggingHandler) {
  @Override
  public void uncaughtException(Thread t, Throwable e) {
  //捕获到异常
     try {
         ......
         //打印crash日志,展示崩溃弹窗等
         // Bring up crash dialog, wait for it to be dismissed
         ActivityManager.getService().handleApplicationCrash(
              mApplicationObject, new ApplicationErrorReport.ParcelableCrashInfo(e));
            } catch (Throwable t2) {
               .... 
            } finally {
                // Try everything to make sure this process goes away.
                Process.killProcess(Process.myPid());//杀死进程
                System.exit(10);
            }
        }
    }  

当异常KillApplicationHandler捕获到异常,进行完一系列处理(主要是打印crash日志,通知AMS展示crash弹窗等)后,最终会杀死进程,这样你的app就崩溃了。

既然都崩溃了,自定义异常捕获器来屏蔽crash真的可行吗?

肯定有人会说,自定义一个异常捕获器,来覆盖掉系统的KillApplicationHandler,然后在捕获到异常后,不杀进程,app就不会崩溃了,就像下面这样,

class MainApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        Thread.setDefaultUncaughtExceptionHandler { _, e ->
            //捕获到异常,只打印日志,不杀进程
            Log.e("MainApplication", "${Thread.currentThread().name} 捕获到异常:${e.message}")
        }
    }
}

这其实只是隔壁老王的思路,虽然确实防护住子线程的crash,但是当主线程出现异常时,app还是无法正常运行。这是因为,当UncaughtExceptionHandler捕获到线程抛出异常的时候,线程在执行完uncaughtException()中的处理后,就无法继续存活了。如果抛异常的线程是主线程,那就意味着主线程会死掉,这时你即便不杀进程,进程活着也没有任何意义了,app还是会停止运行。

把android异常捕获机制在梳理一下,熟悉的同学可以跳过,直接进入下一节。

  • Thread.setCaughtExceptionPreHandler()覆盖所有线程,会在回调DefaultExceptionHandler之前调用;
  • Thread.setCaughtExceptionHandler()同样回覆盖所有线程,可以在应用层被重复调用,并且每一次调用后,都会覆盖上一次设置的DefaultUncaughtExceptionHandler;
  • Thread.currentThread.setUncaughtExceptionHandler(),只可以覆盖当前线程的异常。如果某个线程存在自定义的UncaughtExceptionHandler,回调时会忽略全局的DefaultUncaughtHandler。

既然话都说到这份上了,就请接下never crash大招吧。

要想不crash,只能让线程不要抛出exception,唯此别无他法。如果我们能把一个线程的所有的操作都使用try-catch进行保护,理论上,就能做到app never crash。由于android基于Handler事件驱动的机制,可以在app启动时,向主线程中的MessageQueue中提交一个死循环操作,在这个死循环中不断去poll事件,并且将这个死循环进行try-catch,这样所有主线程中的异常都会被catch住,从而app就再也不会发生crash。

private fun openCrashProtected() {
        Log.d(tag, "openCrashProtected")
        Handler(Looper.getMainLooper()).post {
            while (true) {
                try {
                    Looper.loop()
                    Log.d(tag, "main looper execute loop")
                } catch (e: Throwable) {
                    //所有主线程中的异常都会被catch住,从而不会发生crash
                    Log.e(tag, "catch exception: " + e.message)
                }
            }
        }
    }

有人可能要说了,你这样catch住主线程的异常了,页面可能要乱套哇。话虽如此,但你可以在catch中做业务保护呀。比如,我这里采取的做法是,关闭栈顶activity。 解决ActivityLifeCycle,维护一个Activity栈,

private fun registerLifeCycle() {
        registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
            override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
                ActivityStack.Instance().push(activity)
            }

            override fun onActivityResumed(activity: Activity) {
            }

            override fun onActivityStarted(activity: Activity) {
            }

            override fun onActivityPaused(activity: Activity) {

            }

            override fun onActivityDestroyed(activity: Activity) {
                ActivityStack.Instance().pop(activity)
            }

            override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {

            }

            override fun onActivityStopped(activity: Activity) {
            }
        })
    }

然后当catch住异常时,

//主线程出现异常,关闭栈顶activity
ActivityStack.Instance().curr()?.finish()

github代码

最后奉上github仓库代码,请笑纳。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK