Android 使用自定义UncaughtExceptionHandler处理Firebase崩溃报告和自定义应用程序类

Android 使用自定义UncaughtExceptionHandler处理Firebase崩溃报告和自定义应用程序类,android,firebase,firebase-crash-reporting,Android,Firebase,Firebase Crash Reporting,我的Android应用程序目前使用一个自定义的UncaughtExceptionHandler,旨在捕获任何崩溃,并在手动调用Process.killProcess(Process.myPid())之前,使用AlarmManager计划未来几秒钟的应用程序重启,以避免Android在我的应用程序用例中出现强制关闭弹出窗口,用户将无法与设备交互以点击FC对话框上的“确定”并重新启动应用程序 现在,我想与Firebase崩溃报告集成,但我担心错误行为,所以我的问题如下: 我应该如何生成代码,以便我的

我的Android应用程序目前使用一个自定义的
UncaughtExceptionHandler
,旨在捕获任何崩溃,并在手动调用
Process.killProcess(Process.myPid())
之前,使用
AlarmManager
计划未来几秒钟的应用程序重启,以避免Android在我的应用程序用例中出现强制关闭弹出窗口,用户将无法与设备交互以点击FC对话框上的“确定”并重新启动应用程序

现在,我想与Firebase崩溃报告集成,但我担心错误行为,所以我的问题如下:

  • 我应该如何生成代码,以便我的自定义
    UncaughtExceptionHandler
    在终止其进程之前将异常传递给Firebase崩溃报告?调用
    Thread.getDefaultUncaughtExceptionHandler()
    会给我Firebase崩溃报告
    UncaughtExceptionHandler
    ,这样我就可以调用
    uncaughtException(…)
  • 可以
    Process.killProcess(Process.myPid())
    防止Firebase崩溃报告库执行报告工作吗?Firebase在返回
    uncaughtException(…)
    之前是否会在一个单独的过程中启动崩溃报告?Firebase是否拥有
    UncaughtExceptionHandler
    调用Android默认的
    UncaughtExceptionHandler
    ,显示FC对话框
  • 除了默认进程之外,是否可以
    Process.killProcess(Process.myPid())
    kill Firebase崩溃报告进程
  • 我的自定义
    应用程序
    类如何检测它是否在Firebase崩溃报告过程中实例化?以同样的方式处理这两个过程可能会导致状态不一致

  • 感谢所有试图帮助我的人

    如果在异常处理程序中终止进程,将无法接收崩溃。它将干扰为即时或延迟传输持续碰撞的能力。它可能会干扰任何已注册行为良好的未捕获异常处理程序的库

    事实上,Process.killProcess(Process.myPid())在很大程度上是Android开发的一个工具。Android应用程序不应该关心整个生命周期,如果托管该应用程序的进程失败的话。安卓系统为您管理流程的事实是为用户的利益而设计的优化

    我强烈建议,对于应用程序中未捕获的异常,只需让应用程序像往常一样死掉。掩盖碰撞的适当效果就像在地毯下清扫灰尘一样。您可能正在解决一个短期问题,但真正需要做的是对错误进行正常的日志记录和处理,以便您能够修复它

    不要依赖Firebase崩溃报告在另一个进程中传输异常这一事实。其他进程将在完整的非测试版中删除


    应用程序子类的最佳情况是完全不依赖于它运行的进程。事实上,谷歌的Android团队根本不建议使用应用程序子类,因为它只会给多进程应用程序带来麻烦。如果您必须使用应用程序子类,它应该在多个进程中运行。

    经过一些测试,我终于找到了一种方法来确保我的应用程序在发生
    未捕获异常后正确重新启动。
    我尝试了三种不同的方法,但只有第一种,这是我的原始代码,只需稍加调整即可将未捕获的
    Throwable
    传递到`firebasecash',并确保它被视为致命错误

    有效的代码:

    final UncaughtExceptionHandler crashShield = new UncaughtExceptionHandler() {
    
        private static final int RESTART_APP_REQUEST = 2;
    
        @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            if (BuildConfig.DEBUG) ex.printStackTrace();
            reportFatalCrash(ex);
            restartApp(MyApp.this, 5000L);
        }
    
        private void reportFatalCrash(Throwable exception) {
            FirebaseApp firebaseApp = FirebaseApp.getInstance();
            if (firebaseApp != null) {
                try {
                    FirebaseCrash.getInstance(firebaseApp)
                            .zzg(exception); // Reports the exception as fatal.
                } catch (com.google.firebase.crash.internal.zzb zzb) {
                    Timber.wtf(zzb, "Internal firebase crash reporting error");
                } catch (Throwable t) {
                    Timber.wtf(t, "Unknown error during firebase crash reporting");
                }
            } else Timber.wtf("no FirebaseApp!!");
        }
    
        /**
         * Schedules an app restart with {@link AlarmManager} and exit the process.
         * @param restartDelay in milliseconds. Min 3s to let the user got in settings force
         *                     close the app manually if needed.
         */
        private void restartApp(Context context, @IntRange(from = 3000) long restartDelay) {
            Intent restartReceiver = new Intent(context, StartReceiver_.class)
                    .setAction(StartReceiver.ACTION_RESTART_AFTER_CRASH);
            PendingIntent restartApp = PendingIntent.getBroadcast(
                    context,
                    RESTART_APP_REQUEST,
                    restartReceiver,
                    PendingIntent.FLAG_ONE_SHOT
            );
            final long now = SystemClock.elapsedRealtime();
            // Line below schedules an app restart 5s from now.
            mAlarmManager.set(ELAPSED_REALTIME_WAKEUP, now + restartDelay, restartApp);
            Timber.i("just requested app restart, killing process");
            System.exit(2);
        }
    };
    Thread.setDefaultUncaughtExceptionHandler(crashShield);
    
    解释原因和失败尝试

    奇怪的是,来自
    firebasecash
    类的假设名为
    reportFatal(Throwable ex)
    的方法在静止状态下(谢天谢地)
    public
    ,给了它以下签名:
    zzg(Throwable ex)

    这个方法应该是公开的,但不能被混淆

    为了确保我的应用程序能够与Firebase崩溃报告库引入的多进程正常工作,我必须按照Doug Stevenson的建议,将代码从应用程序类中移出(这是一件好事),并将其放在延迟加载的单进程中,现在它已经准备好了多进程

    您可以看到,在我的代码中没有任何地方,我调用/委托了默认的
    UncaughtExceptionHandler
    ,这将是Firebase崩溃报告。我没有这样做,因为它总是调用默认的一个,这是Android的一个,它有以下问题:

    在我将异常传递给Android默认的
    UncaughtExceptionHandler
    的那一行之后编写的所有代码将永远不会执行,因为调用正在阻塞,进程终止是之后唯一可能发生的事情,除了已经运行的线程

    让应用程序死掉并重新启动的唯一方法是在计划在不久的将来使用
    AlarmManager
    重新启动后,使用
    System.exit(int which)
    process.kill(process.myPid())
    以编程方式终止进程

    有鉴于此,我在调用默认的
    UncaughtExceptionHandler
    之前启动了一个新的
    线程
    ,该线程将在Firebase崩溃报告库获得异常后但在计划的重新启动触发(需要幻数)之前终止正在运行的进程。它第一次起作用,在后台线程终止进程时删除了强制关闭对话框,然后,
    AlarmManager
    唤醒了我的应用程序,让它知道它崩溃了,有机会重新启动

    问题是,第二次不起作用是因为一些模糊和完全没有记录的原因。即使计划重新启动的代码调用
    AlarmManager