Global Exception Handling in Xamarin Cross platform

Ashaar picture Ashaar · Sep 15, 2016 · Viewed 21.6k times · Source

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

Answer

Gerald Versluis picture Gerald Versluis · Sep 15, 2016

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) => {}.