Global Exception Handling in Xamarin Cross platform

28,741

Solution 1

There isn't a 'Xamarin.Forms' way of doing it that I know of. You need to hook into Android and iOS, what you could do is create one method that handles them both in the same way.

A nice post about this is by Peter Norman. He describes that to implement it in Android you can do this in your MainActivity.cs.

// In MainActivity
protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);  

    AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
    TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;  

    Xamarin.Forms.Forms.Init(this, bundle);  
    DisplayCrashReport();  

    var app = new App();  
    LoadApplication(app);
}  

‪#‎region‬ Error handling
private static void TaskSchedulerOnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs unobservedTaskExceptionEventArgs)
{
    var newExc = new Exception("TaskSchedulerOnUnobservedTaskException", unobservedTaskExceptionEventArgs.Exception);
    LogUnhandledException(newExc);
}  

private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs unhandledExceptionEventArgs)
{
    var newExc = new Exception("CurrentDomainOnUnhandledException", unhandledExceptionEventArgs.ExceptionObject as Exception);
    LogUnhandledException(newExc);
}  

internal static void LogUnhandledException(Exception exception)
{
    try
    {
        const string errorFileName = "Fatal.log";
        var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // iOS: Environment.SpecialFolder.Resources
        var errorFilePath = Path.Combine(libraryPath, errorFileName);  
        var errorMessage = String.Format("Time: {0}\r\nError: Unhandled Exception\r\n{1}",
        DateTime.Now, exception.ToString());
        File.WriteAllText(errorFilePath, errorMessage);  

        // Log to Android Device Logging.
        Android.Util.Log.Error("Crash Report", errorMessage);
    }
    catch
    {
        // just suppress any error logging exceptions
    }
}  

/// <summary>
// If there is an unhandled exception, the exception information is diplayed 
// on screen the next time the app is started (only in debug configuration)
/// </summary>
[Conditional("DEBUG")]
private void DisplayCrashReport()
{
    const string errorFilename = "Fatal.log";
    var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
    var errorFilePath = Path.Combine(libraryPath, errorFilename);

    if (!File.Exists(errorFilePath))
    {
        return; 
    }

    var errorText = File.ReadAllText(errorFilePath);
    new AlertDialog.Builder(this)
        .SetPositiveButton("Clear", (sender, args) =>
        {
            File.Delete(errorFilePath);
        })
        .SetNegativeButton("Close", (sender, args) =>
        {
            // User pressed Close.
        })
        .SetMessage(errorText)
        .SetTitle("Crash Report")
        .Show();
} 

‪#‎endregion‬

And for iOS you can add code like this in your AppDelegate.cs.

//iOS: Different than Android. Must be in FinishedLaunching, not in Main.
// In AppDelegate
public override bool FinishedLaunching(UIApplication uiApplication, NSDictionary options)
{
    AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
    TaskScheduler.UnobservedTaskException += TaskSchedulerOnUnobservedTaskException;  
    // Rest of your code...
}  

/// <summary>
// If there is an unhandled exception, the exception information is diplayed 
// on screen the next time the app is started (only in debug configuration)
/// </summary>
[Conditional("DEBUG")]
private static void DisplayCrashReport()
{
    const string errorFilename = "Fatal.log";
    var libraryPath = Environment.GetFolderPath(Environment.SpecialFolder.Resources);
    var errorFilePath = Path.Combine(libraryPath, errorFilename);

    if (!File.Exists(errorFilePath))
    {
        return;
    }

    var errorText = File.ReadAllText(errorFilePath);
    var alertView = new UIAlertView("Crash Report", errorText, null, "Close", "Clear") { UserInteractionEnabled = true };
    alertView.Clicked += (sender, args) =>
    {
        if (args.ButtonIndex != 0)
        {
            File.Delete(errorFilePath);
        }
    };
    alertView.Show();
}

It also includes the ability to show the log when you're debugging the application. Of course you can implement your own logging or handling methods. One thing you could look at is HockeyApp. This handles unhandled exceptions by default and sends them back to you, amongst other things.

Update, since this is still found on Google: For crash reporting and analytics you now want to start looking at App Center. This is the evolvement of HockeyApp and Xamarin Insights (among others like building, distributing and push notifications) and now acts as the mission dashboard for everything that has to do with apps, and not just Xamarin.

For UWP and WinPhone 8.1 there should be a UnhandledException handler in the Application object. Check out this answer for more information. I quote:

For XAML-based apps, you can use UnhandledException; however, that only captures exceptions that come up through the XAML (UI) framework and you don't always get a lot of information about what the root cause is, even in InnerException.

Update for Windows 8.1: UnhandledException also will capture exceptions that are created by an async void method. In Windows 8, such exceptions would just crash the app. LunarFrog has a good discussion of this on their website.

Basically you should add a event handler in the constructor of your App() in App.xaml.cs: this.UnhandledException += (o, s) => {}.

Solution 2

This is what I did for xamarin forms Android global exception handling and app crashes.

This solution will handle both background and foreground exceptions.

AppDomain.CurrentDomain.UnhandledException += (s,e)=>
{
    System.Diagnostics.Debug.WriteLine("AppDomain.CurrentDomain.UnhandledException: {0}. IsTerminating: {1}", e.ExceptionObject, e.IsTerminating);
};

AndroidEnvironment.UnhandledExceptionRaiser += (s, e) =>
{
    System.Diagnostics.Debug.WriteLine("AndroidEnvironment.UnhandledExceptionRaiser: {0}. IsTerminating: {1}", e.Exception, e.Handled);
    e.Handled = true;
};

As a result, I have different behavior for both types of exceptions:

foreground:

background:

Share:
28,741
Ashaar
Author by

Ashaar

Updated on November 12, 2021

Comments

  • Ashaar
    Ashaar over 2 years

    Can you please let me know how to handle the global exception(without App crash) in Xamarin Cross platform project.

  • Ashaar
    Ashaar over 7 years
    Thank you Gerald for your reply, i'll try on this. Just to confirm HockeyApp won't stop the app crashing right, it will only send the error report if we didn't handle the exception right.
  • Gerald Versluis
    Gerald Versluis over 7 years
    That is correct! It only makes sure that it sends back the stacktrace and everything relevant so you can solve the bug before your users know what hit them :) But besides that it can do a lot more! So check it out! Also please accept this as an answer if it helped you!
  • Ashaar
    Ashaar over 7 years
    Hi Gerald, I have done the above changes to Android project in my Visual Studio solution and intentionally throwing some exception within the application unfortunately still the app is crashing without logging any error
  • Ashaar
    Ashaar over 7 years
    Thank you for your prompt response. Sorry for my misleading, I noticed the error is generating now, but the application is going to minimize state. Can you please tell me is there are any way to keep the application focus rather minimize.
  • Gerald Versluis
    Gerald Versluis over 7 years
    I don't think that is possible at that moment. These handlers are not designed to recover from an error, they are a last resort to log some more error information so you, as a developer, can see more of what's going on. If your application reaches this level something went so wrong that you're probably best off restarting the app.
  • Ashaar
    Ashaar over 7 years
    Understood, thank you for your response Gerald. Let me try with IOS and update this as an answer :)
  • Ashaar
    Ashaar over 7 years
    Gerald, I want to apply the same features to UWP and Win 8.1 projects on the same solution, unfortunately it is not supportive. Can you please advice on this.
  • Jurgen Cuschieri
    Jurgen Cuschieri over 5 years
    Do I understand correctly that either way, the app has to crash?!
  • Gerald Versluis
    Gerald Versluis over 5 years
    If you're looking for a solution to not crash your application globally, then this is not the right way. This is only to get some last-minute information out of your app before it crashes. If you have come to the point as described in my answer above, your app has, in fact, already crashed.
  • Jurgen Cuschieri
    Jurgen Cuschieri over 5 years
    I didn't say "Never crash", but I would like to have the option not to crash in some of the scenarios and instead offer an alert to the user. For e.g. calling a web service endpoint, why should I let the app crash if the connection times out?
  • Gerald Versluis
    Gerald Versluis over 5 years
    You don’t. But simply put a try/catch block around a point where it makes sense to catch these errors. Please note that try/catches tend to be very expensive calls. Because of that it would be unwise to handle such connection errors etc. on such a global level. You will quickly run into performance issues.
  • Jurgen Cuschieri
    Jurgen Cuschieri over 5 years
    What do you mean by "where it makes sense to catch these errors"? I am struggling with catching exceptions from creating web service requests or receiving responses. The ideal is to throw an exception in the SendPost method (which communicates with the web service), and catch the exception in the Main method (where the SendPost method is being called). This does not seem to work. Throw simply crashes the app.
  • Gerald Versluis
    Gerald Versluis over 5 years
    By that, I mean that you should put try/catch blocks as close as the possible cause of the exception as possible. Like I said; try/catches are pretty expensive code in terms of performance. Why catching in your app isn't working can only be guessed at this point. It you really need help with that, I would suggest opening a question.
  • CularBytes
    CularBytes about 5 years
    Perhaps add to your code: Microsoft.AppCenter.Crashes.Crashes.TrackError(exception); (android) below your last comment regarding App Center. This works like a charm, thanks!
  • Yisroel M. Olewski
    Yisroel M. Olewski over 4 years
    Hi all. I still have the same issue. No way to simply catch all errors so apps do not crash? Who has an idea that not every web call or other background error should crash the whole app, without littering my entire code base with hundreds of try-catches. I mean this is pretty basic. Thanks!
  • Chucky
    Chucky over 4 years
    Not working on iOS for me. Is there anything I need other than the two events, OnUnhandledException and OnUnobservedTaskException?