Xamarin 跨平台中的全局异常处理

Posted

技术标签:

【中文标题】Xamarin 跨平台中的全局异常处理【英文标题】:Global Exception Handling in Xamarin Cross platform 【发布时间】:2017-01-23 00:34:57 【问题描述】:

能否请您告诉我如何处理 Xamarin 跨平台项目中的全局异常(无 App 崩溃)。

【问题讨论】:

查看 HockeyApp hockeyapp.net 【参考方案1】:

据我所知,没有一种“Xamarin.Forms”的方法。你需要连接到 androidios,你可以做的是创建一种方法来以相同的方式处理它们。

一个很好的post 来自彼得诺曼。他描述了要在 Android 中实现它,您可以在 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\n1",
        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‬

对于 iOS,您可以在 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();

它还包括在您调试应用程序时显示日志的功能。当然,您可以实现自己的日志记录或处理方法。您可以查看的一件事是HockeyApp。默认情况下,这会处理未处理的异常并将它们发送回给您,等等。

更新,因为这仍然可以在 Google 上找到:对于崩溃报告和分析,您现在想开始查看 App Center。这是 HockeyApp 和 Xamarin Insights(其中包括构建、分发和推送通知等)的演变,现在充当与应用相关的所有事情的任务仪表板,而不仅仅是 Xamarin。

对于 UWP 和 WinPhone 8.1,Application 对象中应该有一个 UnhandledException 处理程序。查看this answer 了解更多信息。我引用:

对于基于 XAML 的应用,您可以使用UnhandledException;然而,那 仅捕获通过 XAML (UI) 框架出现的异常 你并不总是能得到很多关于根的信息 原因是,即使在 InnerException 中。

Windows 8.1 更新:UnhandledException 也将捕获 由 async void 方法创建的异常。在 Windows 8 中, 此类异常只会使应用程序崩溃。 LunarFrog 有一个很好的 discussion 在他们的网站上。

基本上你应该在App.xaml.csApp()的构造函数中添加一个事件处理程序:this.UnhandledException += (o, s) =&gt;

【讨论】:

感谢 Gerald 的回复,我会试试这个。只是为了确认 HockeyApp 不会正确阻止应用程序崩溃,如果我们没有正确处理异常,它只会发送错误报告。 感谢您的及时回复。抱歉我的误导,我注意到现在正在生成错误,但应用程序将最小化状态。你能告诉我有什么方法可以保持应用程序的焦点而不是最小化。 我认为那是不可能的。这些处理程序并非旨在从错误中恢复,它们是记录更多错误信息的最后手段,因此作为开发人员,您可以看到更多正在发生的事情。如果您的应用程序达到此级别,则出现严重问题,您最好重新启动应用程序。 大家好。我仍然有同样的问题。没有办法简单地捕获所有错误以使应用程序不会崩溃?谁知道不是每个网络调用或其他后台错误都会使整个应用程序崩溃,而不会在我的整个代码库中乱扔数百个 try-catch。我的意思是这是非常基本的。谢谢! 不适用于我的 iOS。除了 OnUnhandledException 和 OnUnobservedTaskException 这两个事件,我还需要什么吗?【参考方案2】:

这就是我为 xamarin forms Android 全局异常处理和应用程序崩溃所做的。

此解决方案将同时处理后台和前台异常。

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;
;

因此,我对两种类型的异常都有不同的行为:

前景:

背景:

【讨论】:

以上是关于Xamarin 跨平台中的全局异常处理的主要内容,如果未能解决你的问题,请参考以下文章

处理全局异常 Xamarin |机器人 | iOS

在xamarin表单中处理api请求的一个地方处理异常

在一个地方处理 xamarin 表单中 api 请求的异常

Java中的全局异常处理

Java中的全局异常处理

Java中的全局异常处理