We usually initialize Crashlytics in the very early stage in our Application.onCreate() method in main thread. The initialization process of Crashlytics is usually very fast, but it could still take more than 2 frames and as long as 32ms on some devices. Since we are constantly improving our cold start time, we thought about putting the Crashlytics initialization into a background thread and the concern is whether we can still capture a crash before the Crashlytics is initialized.
Our approach here is to set up a CustomDefaultUncaughtExceptionHandler as the first thing and initialize Crashlyatics really early as well but in a background thread. If we got a crash before the Crashlytics is initialized or to be initialized, we will reinitialize Crashlytics in the CustomDefaultUncaughtExceptionHandler and use reflection to get CrashlyticsCore's CrashlyticsUncaughtExceptionHandler and then log this uncaught exception.
Our ask is whether it's possible to make a public method for us to log a uncaught exception so that we don't have to use reflection. Or if there are some other suggestions for us to offload the Crashlytics initialization to a background thread but also be able to catch early exceptions.
The pseudo code looks like the following:
public void uncaughtException(Thread thread, Throwable ex) {
try {
if (_crashlyticsEnabled) {
if (!_crashlyticsInitialized) {
// IMPORTANT: in case we encounter a crash before the crashlytics is
// initialized, we can initialize crashlytics here and send this crash
// to the server right away.
initCrashlytics(_context);
Crashlytics.logUncaughtException(thread, ex); // API we are asking
}
}
} catch (Exception e) {
PLog.error(e, "Exception occurred during ...");
} finally {
_defaultExceptionHandler.uncaughtException(thread, ex);
}
}
Related
I'm trying to implement an uncaught exception handler in an Android only app built with Xamarin for logging purposes.
AppDomain.CurrentDomain.UnhandledException += (o, e) => exLogger.UncaughtException((Exception)e.ExceptionObject);
TaskScheduler.UnobservedTaskException += (o, e) => exLogger.UncaughtException(e.Exception);
and
public void UncaughtException(Exception ex)
{
try
{
Log.Error(TAG, "Exception caught: {0}", ex.Message ?? "Unknown Exception");
var di = Directory.CreateDirectory(Path.Combine(context.FilesDir.AbsolutePath, TAG));
//Rest of Handler - saves error to file system, attempts to upload to a server
...
}
catch (Exception)
{
Log.Error(TAG, "Exception handler failed");
}
}
The handler in question works with zero problems when I implement it in a very simple "Hello World" application, where I trigger an exception on button-press:
throw new Exception("this is a test exception");
(or similar).
However, When I do the same thing in the application I am actually developing, the app crashes inside the handler, printing no further information as to why this happens.
When I step through my code, the debugger hits a breakpoint at the entry to the handler - e.g. within the try block, but before the Log.Error line. However, attempting to step further than this point results in immediate app crash, without either the Log.Debug line in the try OR catch block being executed - nothing is printed to the ADB Logger. Additionally, the error printed to the ADB Log by W/system.err is only the error I intentionally caused (to trigger the handler) - no information on the crash WITHIN the handler is provided.
Does anyone have any idea what could be causing this crash, or even advice on how to get more information from the ADB logs on what the crash within the handler was caused by? My only train of thought is that, because my app is a WebView application, potentially the WebView itself is consuming the Exception in some manner? Any help is greatly appreciated!
Is there a way to execute a method after the App crashed (a certain number times)?
Example: Sometimes with every run data changes in a way that the app does not crash anymore. So, this way it may recover itself after trying to start it for 2 times for example. The 3rd time would run smoothly again. Just an example!
My only idea right now would be to wrap everything inside the onCreate method in my main Activity in a generic try-catch block (catching Exception) handler. I don't think this is smart for several reasons, for example performance.
To catch every uncaught exception you can use following snippet:
public class App extends Application {
public void onCreate() {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread t, Throwable e) {
Log.e("TAG","Implement your recovery strategy here, e.g. clean database or cache: " + e.toString());
}
});
}
}
Please note, that this is very 'hacky' solution, and if your app is in corrupted state very often there is probably something wrong with your code.
I am working on an Android library where I need to perform some action if an uncaught exception is seen.
I am successfully setting my uncaught exception handler and running the code I need, which is to post some information to a web server, but after my part is finished I want the app to then do what Android usually does, i.e. display a dialogue to the informing them that it has crashed and then exit the app, and post the details to the Google Play Developer Console.
At the moment, my uncaught exception successfully posts to the server, but then keeps the app running but in a bit of a weird state as the thread has party disappeared, where usually, if my an uncaught exception is thrown, then Android closes the app.
Below is how I am doing my uncaught exception handler:
private static void setUnhandledExceptionHandler()
{
Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler()
{
#Override
public void uncaughtException(final Thread thread, final Throwable) {
CrashReporter.ReportUnhandledCrash(((Exception) ex));
Looper.loop();
}
});
}
Basically, what I want to do is, have my app post to to my server via my unhandled exception handler and then quit the app, in the same way that Android usually does, i.e. display a force close error the use and close the app.
I had a similar problem when tracking to GoogleAnalytics - most Exceptions got lost when i tried to report them in the default handler. So i cached them and reported them on the next startup.
The second trick (and this hopefully answers your question) to let the Application crash as it should is to store the 'old' DefaultUncaughtExceptionHandler and pass the exception to it.
The onCreate method below is the one of my Application class
#Override
public void onCreate()
{
super.onCreate();
trackCachedException();
setupExceptionHandling();
}
private void setupExceptionHandling()
{
_systemExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.UncaughtExceptionHandler myHandler = new Thread.UncaughtExceptionHandler()
{
#Override
public void uncaughtException(Thread thread, Throwable ex)
{
cacheException(thread.getName(), ex);
if (_systemExceptionHandler != null)
{
_systemExceptionHandler.uncaughtException(thread, ex);
}
}
};
// Make myHandler the new default uncaught exception handler.
Thread.setDefaultUncaughtExceptionHandler(myHandler);
}
The only way I can see how you can do this is neither clean nor pleasant, so I'll say sorry before we begin...
The order of operations is:
Store the uncaught exception
Log the exception as you have done previously
Deregister your uncaught exception handler, by calling the set method with null.
Launch a new thread that throws your stored exception.
You are effectively throwing the same exception twice, but removing the recovery mechanism on the second throw.
This GoogleAuthUtil getToken() call:
String token = GoogleAuthUtil.getToken(appContext, accountName, scope);
occasionally fails with this exception:
java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare():
ak: GooglePlayServicesNotAvailable
at com.google.android.gms.auth.GoogleAuthUtil.a(Unknown Source)
at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
at com.google.android.gms.auth.GoogleAuthUtil.getToken(Unknown Source)
So (duh) obviously something is Google's code is trying to create a Handler :) But we are calling getToken() from a standard (non-Looper) thread with all the recommended exception handling per Google's documentation and the documentation explicitly says "Example of how to use the GoogleAuthUtil in a blocking, non-main thread context". So e.g. it certainly should not be called on the UI thread.
One thing is ambiguous in the gplay docs: We are passing the Application context to getToken(), but the docs don't say if it expects a specific context e.g. from an Activity. Anyone else have experience one way or the other? I don't see how this could cause the problem but you never know.
The MAIN question: how to recover? Currently we catch the exception and give up, but that does mean we are failing to get a auth for the affected users.
As always guidance from a Googler would be appreciated :)
Thanks!
I think we have figured this out. The RuntimeException is from getErrorDialog() while handling GooglePlayServicesAvailabilityException; something about the exception handling was excluding that from the stack trace. This is why the failure is so rare -- only unusual device configs throw that exception.
WRONG: like google's documentation example our handler did this:
} catch (GooglePlayServicesAvailabilityException e) {
int errorCode = e.getConnectionStatusCode();
if (GooglePlayServicesUtil.isUserRecoverableError(errorCode)) {
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode, resolutionActivity, ...);
but that does not work because you cannot call getErrorDialog() on a non-Looper thread.
FIX:
Activity resolutionActivity = ...;
try {
String token = GoogleAuthUtil.getToken(appContext, accountName, scope);
...
} catch (GooglePlayServicesAvailabilityException e) {
final int errorCode = e.getConnectionStatusCode();
if (GooglePlayServicesUtil.isUserRecoverableError(errorCode)) {
resolutionActivity.runOnUiThread(new Runnable() {
#Override public void run() {
Dialog errorDialog = GooglePlayServicesUtil.getErrorDialog(errorCode, resolutionActivity, ...);
errorDialog.show();
I will update after we have released this and confirmed it is the cause, but it seems likely.
If this is the problem then Google's documentation should be updated as it shows calling getErrorDialog() on a background thread.
We shipped the fix above and problem is solved: GooglePlayServicesUtil.getErrorDialog() should be called on UI thread.
In my Android application I utilize setDefaultUncaughtExceptionHandler to store information about unhandled exceptions locally on a user device. After some feedback I suspect that this code prevents the built-in Google's error-reporting feature from work, because I do not see error reports in the developer console, while exceptions are reported by users. Their devices are well past 2.2, where the error-reporting was introduced. Could it be that specific device with, say, 4.0.3 does not support this feature? If yes, how can I detect this programmatically?
I can't find information regarding this in Android documentation. I'd like both standard error-reporting and my custom handling work together. In my custom exception handler I call Thread.getDefaultUncaughtExceptionHandler() to get default handler, and in my implementation of uncaughtException I propagate exception to this default handler as well.
I first tried calling System.exit(1); as mentioned in this SO answer, but that didn't work.
Finally solved it by calling the uncaughtException(Thread thread, Throwable ex) again on Androids default UncaughtExceptionHandler (found it by checking the ACRA source code.
Example Activity
public class MainActivity extends Activity implements Thread.UncaughtExceptionHandler {
private Thread.UncaughtExceptionHandler _androidUncaughtExceptionHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
_androidUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
Thread.setDefaultUncaughtExceptionHandler(this);
// Rest onCreate
setContentView(R.layout.main_activity);
}
//#Override
public void uncaughtException(Thread thread, Throwable ex) {
try {
// Do your stuff with the exception
} catch (Exception e) {
/* Ignore */
} finally {
// Let Android show the default error dialog
_androidUncaughtExceptionHandler.uncaughtException(thread, ex);
}
}
}
Yes, this will stop the inbuilt error report. The user is given a dialog when your app crashes, with an option to report the error via Google Play. However, if you use setDefaultUncaughtExceptionHandler() then the exception is handled within your app, and no option is given to report it.
I recommend that you integrate ACRA into your project, as it allows you to easily receive error reports upon crashes.