使用自定义 UncaughtExceptionHandler 处理 Firebase 崩溃报告和自定义应用程序 class
Deal with Firebase crash reporting and custom Application class with custom UncaughtExceptionHandler
我的 Android 应用目前使用自定义 UncaughtExceptionHandler
旨在捕获任何崩溃,并在手动调用 Process.killProcess(Process.myPid())
以避免 Android 的强制关闭弹出窗口,如在我的应用程序的用例中,用户将无法与设备交互以点击 FC 对话框上的 "ok" 并重新启动应用程序。
现在,我想与 Firebase 崩溃报告集成,但我担心错误的行为,所以这是我的问题:
- 我应该如何编写代码,以便我的自定义
UncaughtExceptionHandler
在终止进程之前将异常传递给 Firebase 崩溃报告?调用 Thread.getDefaultUncaughtExceptionHandler()
会给我 Firebase 崩溃报告 UncaughtExceptionHandler
这样我就可以调用 uncaughtException(...)
吗?
- 可以
Process.killProcess(Process.myPid())
阻止 Firebase 崩溃报告库进行报告工作吗? Firebase 会在 uncaughtException(...)
returns 之前在单独的进程中启动它的崩溃报告吗? Firebase 是否拥有 UncaughtExceptionHandler
回调 Android 默认值 UncaughtExceptionHandler
,显示 FC 对话框?
- 除了默认进程之外,可以
Process.killProcess(Process.myPid())
杀死 Firebase 崩溃报告进程吗?
- 我的自定义
Application
class 如何检测它是否在 Firebase 崩溃报告进程中实例化?以相同的方式处理这两个进程可能会导致不一致的状态。
感谢任何试图帮助我的人!
如果您终止异常处理程序中的进程,您将无法收到崩溃。它将干扰为即时或延迟传输持续崩溃的能力。它可能会干扰 任何 已注册未捕获异常处理程序且表现良好的库。
事实上,Process.killProcess(Process.myPid()) 是 Android 开发的 anti-pattern。 Android 如果托管应用程序的进程,应用程序根本不应该关心整个生命周期。 Android 为您管理流程这一事实是为了用户的利益而设计的优化。
对于应用中未捕获的异常,我强烈建议让应用像往常一样死掉。掩盖崩溃的适当影响就像扫地毯下的污垢一样。您可能正在解决一个短期问题,但真正需要发生的是正常记录和处理错误,以便您可以修复它。
不要依赖于 Firebase 崩溃报告在另一个进程中传输异常这一事实。其他进程将在完整的非测试版中删除。
您的 Application 子类的最佳情况是完全不依赖于它正在运行的进程。事实上,Google 的 Android 团队根本不推荐使用 Application 子类,因为它只会给多进程应用程序带来麻烦。如果您必须使用 Application 子类,它应该期望在多个进程中 运行。
经过一些测试,我终于找到了一种方法来确保我的应用程序在 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);
原因和不成功尝试的解释
奇怪的是 FirebaseCrash
class 中假设命名的 reportFatal(Throwable ex)
方法的名称在静止(谢天谢地)public
时被保护,给它以下签名:zzg(Throwable ex)
。
此方法应保留 public,但恕我直言,不要混淆。
为了确保我的应用程序在 Firebase 崩溃报告库引入的多进程下正常工作,我不得不将代码从应用程序中移走 class(这很棒)并将其放入延迟加载的单例中相反,按照 Doug Stevenson 的建议,它现在可以进行多进程处理了。
你可以在我的代码中的任何地方看到,我 called/delegated 到默认值 UncaughtExceptionHandler
,这将是 Firebase 崩溃报告之一。我没有这样做,因为它总是调用默认的,即 Android 的那个,它有以下问题:
在我将异常传递给 Android 的默认值 UncaughtExceptionHandler
之后编写的所有代码将永远不会执行,因为调用是阻塞的,进程终止是唯一可以执行的发生在之后,除了已经 运行 个线程。
让应用死掉并重启的唯一方法是在不久的将来用 AlarmManager
安排重启后,用 System.exit(int whatever)
或 Process.kill(Process.myPid())
以编程方式终止进程。
鉴于此,我在调用默认 UncaughtExceptionHandler
之前启动了一个新的 Thread
,这会在 Firebase 崩溃报告库获得异常之后终止 运行ning 进程,但之前预定的重启会触发 (需要幻数)。它第一次工作,当后台线程终止进程时删除强制关闭对话框,然后 AlarmManager
唤醒我的应用程序,让它知道它崩溃并有机会重新启动。
问题是第二次没有成功,原因是一些不明确的 绝对没有记录的 原因。即使调用 AlarmManager
安排重启的代码是正确的 运行,应用程序也永远不会重启。
此外,Force Close 弹出窗口永远不会出现。在查看是否包含 Firebase 崩溃报告(因此自动启用)后并没有改变此行为的任何内容后,它被绑定到 Android(我在 Kyocera KC-S701 运行ning Android 4.4.2).
所以我最终搜索了 Firebase 自己的 UncaughtExceptionHandler
调用来报告 throwable 的内容,发现我可以自己调用代码并管理我的应用程序在未捕获的 Throwable
.[=32 上的行为方式=]
Firebase 如何改进此类场景
使假设命名的 reportFatal(Throwable ex)
方法不进行名称混淆和记录,或者让我们决定在 Firebase 在 UncaughtExceptionHandler
中捕获 Throwable
之后会发生什么,而不是不灵活地委托给愚蠢的 Android 的默认 UncaughtExceptionHandler
会有很大帮助。
这将允许在 Android 上开发 运行 的关键应用程序的开发人员确保他们的应用程序在用户无法做到的情况下保持 运行ning(想想医疗监控应用程序) 、监控应用等)。
它还允许开发人员启动自定义 activity 来要求用户解释它是如何发生的,并能够 post 屏幕截图等
顺便说一句,我的应用程序旨在监控危急情况下的人类福祉,因此不能容忍停止 运行ning。必须恢复所有异常以确保用户安全。
我的 Android 应用目前使用自定义 UncaughtExceptionHandler
旨在捕获任何崩溃,并在手动调用 Process.killProcess(Process.myPid())
以避免 Android 的强制关闭弹出窗口,如在我的应用程序的用例中,用户将无法与设备交互以点击 FC 对话框上的 "ok" 并重新启动应用程序。
现在,我想与 Firebase 崩溃报告集成,但我担心错误的行为,所以这是我的问题:
- 我应该如何编写代码,以便我的自定义
UncaughtExceptionHandler
在终止进程之前将异常传递给 Firebase 崩溃报告?调用Thread.getDefaultUncaughtExceptionHandler()
会给我 Firebase 崩溃报告UncaughtExceptionHandler
这样我就可以调用uncaughtException(...)
吗? - 可以
Process.killProcess(Process.myPid())
阻止 Firebase 崩溃报告库进行报告工作吗? Firebase 会在uncaughtException(...)
returns 之前在单独的进程中启动它的崩溃报告吗? Firebase 是否拥有UncaughtExceptionHandler
回调 Android 默认值UncaughtExceptionHandler
,显示 FC 对话框? - 除了默认进程之外,可以
Process.killProcess(Process.myPid())
杀死 Firebase 崩溃报告进程吗? - 我的自定义
Application
class 如何检测它是否在 Firebase 崩溃报告进程中实例化?以相同的方式处理这两个进程可能会导致不一致的状态。
感谢任何试图帮助我的人!
如果您终止异常处理程序中的进程,您将无法收到崩溃。它将干扰为即时或延迟传输持续崩溃的能力。它可能会干扰 任何 已注册未捕获异常处理程序且表现良好的库。
事实上,Process.killProcess(Process.myPid()) 是 Android 开发的 anti-pattern。 Android 如果托管应用程序的进程,应用程序根本不应该关心整个生命周期。 Android 为您管理流程这一事实是为了用户的利益而设计的优化。
对于应用中未捕获的异常,我强烈建议让应用像往常一样死掉。掩盖崩溃的适当影响就像扫地毯下的污垢一样。您可能正在解决一个短期问题,但真正需要发生的是正常记录和处理错误,以便您可以修复它。
不要依赖于 Firebase 崩溃报告在另一个进程中传输异常这一事实。其他进程将在完整的非测试版中删除。
您的 Application 子类的最佳情况是完全不依赖于它正在运行的进程。事实上,Google 的 Android 团队根本不推荐使用 Application 子类,因为它只会给多进程应用程序带来麻烦。如果您必须使用 Application 子类,它应该期望在多个进程中 运行。
经过一些测试,我终于找到了一种方法来确保我的应用程序在 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);
原因和不成功尝试的解释
奇怪的是 FirebaseCrash
class 中假设命名的 reportFatal(Throwable ex)
方法的名称在静止(谢天谢地)public
时被保护,给它以下签名:zzg(Throwable ex)
。
此方法应保留 public,但恕我直言,不要混淆。
为了确保我的应用程序在 Firebase 崩溃报告库引入的多进程下正常工作,我不得不将代码从应用程序中移走 class(这很棒)并将其放入延迟加载的单例中相反,按照 Doug Stevenson 的建议,它现在可以进行多进程处理了。
你可以在我的代码中的任何地方看到,我 called/delegated 到默认值 UncaughtExceptionHandler
,这将是 Firebase 崩溃报告之一。我没有这样做,因为它总是调用默认的,即 Android 的那个,它有以下问题:
在我将异常传递给 Android 的默认值 UncaughtExceptionHandler
之后编写的所有代码将永远不会执行,因为调用是阻塞的,进程终止是唯一可以执行的发生在之后,除了已经 运行 个线程。
让应用死掉并重启的唯一方法是在不久的将来用 AlarmManager
安排重启后,用 System.exit(int whatever)
或 Process.kill(Process.myPid())
以编程方式终止进程。
鉴于此,我在调用默认 UncaughtExceptionHandler
之前启动了一个新的 Thread
,这会在 Firebase 崩溃报告库获得异常之后终止 运行ning 进程,但之前预定的重启会触发 (需要幻数)。它第一次工作,当后台线程终止进程时删除强制关闭对话框,然后 AlarmManager
唤醒我的应用程序,让它知道它崩溃并有机会重新启动。
问题是第二次没有成功,原因是一些不明确的 绝对没有记录的 原因。即使调用 AlarmManager
安排重启的代码是正确的 运行,应用程序也永远不会重启。
此外,Force Close 弹出窗口永远不会出现。在查看是否包含 Firebase 崩溃报告(因此自动启用)后并没有改变此行为的任何内容后,它被绑定到 Android(我在 Kyocera KC-S701 运行ning Android 4.4.2).
所以我最终搜索了 Firebase 自己的 UncaughtExceptionHandler
调用来报告 throwable 的内容,发现我可以自己调用代码并管理我的应用程序在未捕获的 Throwable
.[=32 上的行为方式=]
Firebase 如何改进此类场景
使假设命名的 reportFatal(Throwable ex)
方法不进行名称混淆和记录,或者让我们决定在 Firebase 在 UncaughtExceptionHandler
中捕获 Throwable
之后会发生什么,而不是不灵活地委托给愚蠢的 Android 的默认 UncaughtExceptionHandler
会有很大帮助。
这将允许在 Android 上开发 运行 的关键应用程序的开发人员确保他们的应用程序在用户无法做到的情况下保持 运行ning(想想医疗监控应用程序) 、监控应用等)。
它还允许开发人员启动自定义 activity 来要求用户解释它是如何发生的,并能够 post 屏幕截图等
顺便说一句,我的应用程序旨在监控危急情况下的人类福祉,因此不能容忍停止 运行ning。必须恢复所有异常以确保用户安全。