使用自定义UncaughtExceptionHandler处理Firebase崩溃报告和自定义Application类

时间:2016-06-27 07:38:27

标签: android firebase firebase-crash-reporting

我的Android应用目前使用自定义UncaughtExceptionHandler,旨在捕获任何崩溃,并在AlarmManager之前安排应用重启几秒钟,然后手动调用Process.killProcess(Process.myPid())以避免Android& #39; s强制关闭弹出窗口,就像在我的应用程序的用例中一样,用户将无法与设备进行交互以轻拍"确定"在FC对话框上,然后重新启动应用程序。

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

  1. 如何制作我的代码,以便我的自定义UncaughtExceptionHandler将异常传递给Firebase崩溃报告,然后再将其删除?是否会致电Thread.getDefaultUncaughtExceptionHandler()向我提供Firebase崩溃报告UncaughtExceptionHandler,以便我可以在其上拨打uncaughtException(...)
  2. 可能会Process.killProcess(Process.myPid())阻止Firebase崩溃报告库执行此报告工作吗? Firebase会在uncaughtException(...)返回之前的分离过程中启动崩溃报告吗? Firebase是否拥有UncaughtExceptionHandler来自Android默认UncaughtExceptionHandler的电话,显示了FC对话框?
  3. 除了默认流程之外,
  4. 可能会Process.killProcess(Process.myPid())杀死Firebase崩溃报告流程吗?
  5. 我的自定义Application类如何检测它是否在Firebase崩溃报告流程中实例化?以相同的方式处理这两个过程可能会导致状态不一致。
  6. 感谢任何试图帮助我的人!

2 个答案:

答案 0 :(得分:3)

如果您在异常处理程序中终止进程,则无法接收崩溃。它会干扰直接或延迟传输的持续崩溃能力。它可能会干扰已注册未捕获的异常处理程序的任何库。

事实上,Process.killProcess(Process.myPid())非常适合Android开发anti-pattern。如果承载应用程序的进程,Android应用程序根本不应该关注整个生命周期。 Android为您管理流程的事实是为用户的利益而设计的优化。

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

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

Application子类的最佳情况是不依赖于它运行的进程。实际上,Google的Android团队根本不建议使用Application子类,因为它只会导致多进程应用程序出现问题。如果必须使用Application子类,则应该在多个进程中运行。

答案 1 :(得分:0)

经过一些测试后,我终于找到了一种确保我的应用在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的建议,现在已经准备好了多个流程。

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

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

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

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

问题在于,第二次因某些模糊和绝对无证的原因而无效。即使安排调用AlarmManager的重新启动的代码正确运行,应用程序也永远不会重新启动。

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

所以我终于搜索了Firebase自己调用的UncaughtExceptionHandler来报告throwable并看到我可以自己调用代码并自行管理我的应用程序在未被捕获的Throwable上的行为。

Firebase如何改善此类情景 将假设命名的reportFatal(Throwable ex)方法设置为非名称模糊和记录,或者让我们决定在Firebase捕获Throwable UncaughtExceptionHandler之后发生的事情,而不是强行委托给哑巴的默认{{} 1}}会有很多帮助。

这将允许开发在Android上运行的关键应用程序的开发人员确保他们的应用程序在用户无法运行时继续运行(考虑医疗监控应用程序,监控应用程序等)。

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

顺便说一下,我的应用程序旨在监控人们在危急情况下的幸福感,因此无法忍受停止运行。必须恢复所有例外以确保用户安全。