经过一些测试,我终于找到了一种方法来确保我的应用程序在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 上运行的关键应用程序的开发人员确保他们的应用程序在用户无法运行的情况下继续运行(想想医疗监控应用程序、监控应用程序等)。
它还允许开发人员启动自定义活动,要求用户解释它是如何发生的,并能够发布屏幕截图等。
顺便说一句,我的应用程序旨在监控人类在危急情况下的幸福感,因此不能容忍停止运行。必须恢复所有异常以确保用户安全。