2

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

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

  1. 我应该如何制作我的代码,以便我的自定义UncaughtExceptionHandler在终止它的进程之前将异常传递给 Firebase 崩溃报告?打电话Thread.getDefaultUncaughtExceptionHandler()会给我 Firebase 崩溃报告UncaughtExceptionHandler,以便我可以打电话给我uncaughtException(...)吗?
  2. 可能Process.killProcess(Process.myPid())会阻止 Firebase 崩溃报告库进行报告工作?Firebase 会在返回之前在单独的进程中启动它的崩溃报告uncaughtException(...)吗?Firebase 是否自己UncaughtExceptionHandler回调 Android 默认值UncaughtExceptionHandler,显示 FC 对话框?
  3. Process.killProcess(Process.myPid())除了默认进程之外,还可以杀死 Firebase 崩溃报告进程吗?
  4. 我的自定义Application类如何检测它是否在 Firebase 崩溃报告过程中被实例化?以相同的方式处理这两个过程可能会导致不一致的状态。

感谢任何试图帮助我的人!

4

2 回答 2

3

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

事实上,Process.killProcess(Process.myPid()) 在很大程度上是 Android 开发的反模式。如果托管应用程序的进程,Android 应用程序根本不应该关心整个生命周期。Android 为您管理流程这一事实是为用户利益而设计的优化。

对于应用程序中未捕获的异常,我强烈建议让应用程序像往常一样死掉。掩盖碰撞的适当影响就像在地毯下扫除污垢。您可能正在解决一个短期问题,但真正需要的是正常记录和处理错误,以便您可以修复它。

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

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

于 2016-06-28T06:41:09.923 回答
2

经过一些测试,我终于找到了一种方法来确保我的应用程序在UncaughtException. 我尝试了三种不同的方法,但只有第一种是我的原始代码,只需稍加调整即可将未捕获的内容传递Throwable给 FirebaseCrash,并确保将其视为致命错误。

有效的代码:

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);

解释为什么和不成功的尝试

奇怪的是,类中假设命名的reportFatal(Throwable ex)方法FirebaseCrash在静止时(谢天谢地)对其名称进行public了保护,并赋予它以下签名:zzg(Throwable ex).

这种方法应该保持公开,但不会被混淆恕我直言。

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

您可以在我的代码中看到任何地方,我调用/委派了 default UncaughtExceptionHandler,这将是 Firebase Crash Reporting 之一。我没有这样做,因为它总是调用默认的,即 Android 的,它有以下问题:

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

让应用程序死掉并重新启动的唯一方法是通过在不久的将来安排重新启动System.exit(int whatever)Process.kill(Process.myPid())在安排重新启动之后以编程方式终止进程。AlarmManager

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

问题是第二次没有工作,因为一些模糊的和绝对没有记录的原因。即使安排重新启动调用的代码AlarmManager 已正确运行,该应用程序也永远不会重新启动。

此外,强制关闭弹出窗口永远不会出现。在看到是否包含 Firebase 崩溃报告(因此自动启用)并没有改变有关此行为的任何内容后,它与 Android 相关联(我在运行 Android 4.4.2 的 Kyocera KC-S701 上进行了测试)。

所以我最终搜索了 Firebase 自己UncaughtExceptionHandler调用的东西来报告 throwable,发现我可以自己调用代码并管理我自己的应用程序在未捕获的Throwable.

Firebase 如何改进此类场景 使假设命名的reportFatal(Throwable ex)方法不进行名称混淆和记录,或者让我们决定在 Firebase 捕获Throwable它之后会发生什么,UncaughtExceptionHandler而不是不灵活地委托给愚蠢的 Android 的默认值UncaughtExceptionHandler将有很大帮助。

它将允许开发在 Android 上运行的关键应用程序的开发人员确保他们的应用程序在用户无法运行的情况下继续运行(想想医疗监控应用程序、监控应用程序等)。

它还允许开发人员启动自定义活动,要求用户解释它是如何发生的,并能够发布屏幕截图等。

顺便说一句,我的应用程序旨在监控人类在危急情况下的幸福感,因此不能容忍停止运行。必须恢复所有异常以确保用户安全。

于 2016-07-11T15:29:54.293 回答