Manejo de errores globales en Xamarin Forms

Contenido

Muchas veces nos podemos encontrar con excepciones no controladas que son muy difíciles de detectar y registrar, y debe hacerlo para poder manejar los errores en su aplicación. Un enfoque es usar App Center, pero siempre es ideal poder registrar que paso para luego manejar la información o enviarla a servicio web.

💡 TIP: Las excepciones se manejan mejor cerca del punto en el que se lanzan.

Existen controladores globales en cada plataforma para permitir recibir notificaciones de excepciones que no hayas manejado en otro lugar. Ten en cuenta que estas NO existen para permitirte detectar una excepción y continuar la ejecución como si nada hubiera pasado.

Si no manejas las excepciones cerca de donde se lanzaron y se activa uno de los controladores globales, no espere poder recuperarte. Es probable que su aplicación se encuentre en un estado desconocido en ese momento, por lo que es probable que no sea seguro continuar con la ejecución.

⚠ Precaución: El manejo de excepciones es mejor agregarlo al principio del desarrollo. Intentar adaptarlo al final del desarrollo será doloroso.

Errores globales en Xamarin Forms

El siguiente código es el que usamos en este momento y nos ha ayudado mucho, especialmente las excepciones de tareas no observadas que son simplemente silenciosas, es decir, que no bloquean la aplicación, pero aún quieres manejarlas.

🚀 Nota: El código mostrado a continuación también incluye la capacidad de mostrar el registro cuando está depurando la aplicación. Por supuesto, puede implementar sus propios métodos de registro o manejo.

Android

...
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‬  
...

⚠ Nota: TaskScheduler.UnobservedTaskException no se dispara inmediatamente. Puede que tenga que esperar un poco antes de que se dispare.

iOS

A diferencia de Android debes utilizar el método FinishedLaunching, y no el principal en el AppDelegate.cs.

...
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();
}
...

UWP

En UWP podemos utilizar UnhandledException; sin embargo, solo captura las excepciones que surgen a través del marco XAML (UI) y no siempre obtienes mucha información sobre cuál es la causa raíz, incluso en InnerException.

...
public App()
{
    // Global handler for uncaught exceptions.
    UnhandledException += Application_UnhandledException;

    InitializeComponent();
}

private void Application_UnhandledException(object sender, ApplicationUnhandledExceptionEventArgs e)
{
    if (Debugger.IsAttached)
    {
        // An unhandled exception has occurred; break into the debugger
        Debugger.Break();
    }
}
...

Lo que deberías tener en cuenta

Excepciones administradas

Para las excepciones administradas, debe ocuparse de eso usted mismo (en el nivel superior de la interfaz de usuario) y dejar que las capas inferiores arrojen las excepciones nuevamente a través del stack.

De igual manera, puedes tener una función de ejecución asíncrona genérica en su BaseViewModel que maneje excepciones de manera estandarizada. En otras excepciones donde necesita cambiar el comportamiento de UX en función de una excepción, implemente try-catch para esa área.

Si quieres saber mas sobre este tema, solo déjame saber en las redes.

App center

Hay que tener en cuenta que en la mayoría de los casos AppCenter detectará automáticamente las excepciones no administradas, y también detecta fallas al reiniciar.

Por otro lado, tratar de detectar excepciones en un nivel específico de la plataforma puede tardar un poco y la aplicación puede estar en un estado demasiado inestable para continuar en muchos casos.

🚨 Nota: Ten en cuenta que en algunos casos AppCenter no podrá detectar todas las excepciones, aunque esto se pueda detectar globalmente. Son casos muy específicos, donde cualquier cosa puede incidir pero es bueno mencionarlo.

Conclusión

Espero que este articulo les sea de mucha utilidad, y cualquier comentario me lo dejan en la caja de abajo. Si quieres una segunda parte sobre como manejar tus errores en tu lógica de negocio globalmente déjamelo saber en las redes y entérate de todo el contenido que estoy compartiendo.

Sin nada mas que agregar, ¡Nos vemos en la próxima!

¿Qué opinas de este contenido?
 
Luis Matos

Luis Matos

I help professionals and companies to create value solutions. I am a Systems Engineer, blockchain executive, and international mobile application speaker. Founder of the Malla Consulting Agency and several international technology communities.
Suscribirte
Notificar de
0 Comments
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x

Buscar en el sitio