如何防止iOS崩溃记者崩溃的MonoTouch应用程序?
iOS中有很多iOS崩溃报告库,包括TestFlight和HockeyApp 。 如果你不想依靠服务,你仍然可以使用像PLCrashReporter这样的库 。 绑定这些库是相当微不足道的,因为它们的公共API通常由几个具有几个初始化方法的类组成。
但是,当我们的应用程序尝试使用TestFlight和后来的HockeyApp时,我们的应用程序开始随机崩溃。 原来,这是一个多次 报道的已知问题,但Xamarin并没有提出警告,这是相对模糊的,我们发现它是困难的。
我们已经了解到, 所有的iOS崩溃logging器都会阻止Mono捕获空引用exception:
try { object o = null; o.GetHashCode (); } catch { // Catch block isn't called with crash reporting enabled. // Instead, the app will crash. }
为什么会这样呢? 引用Xamarin开发者Rolf的话说,
空引用exception实际上是一个SIGSEGV信号。 通常单声道运行时间处理这个并将其转换为空引用exception,允许执行继续。 问题是SIGSEGV信号在ObjC应用程序中是一件非常糟糕的事情(当它出现在托pipe代码之外时),所以任何崩溃报告解决scheme都会将其报告为崩溃(并且杀死应用程序) – 这发生在MonoTouch获得一个机会之前来处理SIGSEGV,所以MonoTouch没有办法做到这一点。
我确定许多在MonoTouch应用程序中使用TestFlight,而不知道它会导致崩溃。
这是不是很讽刺?
如何使崩溃报告库不会使MonoTouch应用程序崩溃?
把它放在AppDelegate.cs
:
[DllImport ("libc")] private static extern int sigaction (Signal sig, IntPtr act, IntPtr oact); enum Signal { SIGBUS = 10, SIGSEGV = 11 } static void EnableCrashReporting () { IntPtr sigbus = Marshal.AllocHGlobal (512); IntPtr sigsegv = Marshal.AllocHGlobal (512); // Store Mono SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, IntPtr.Zero, sigbus); sigaction (Signal.SIGSEGV, IntPtr.Zero, sigsegv); // Enable crash reporting libraries EnableCrashReportingUnsafe (); // Restore Mono SIGSEGV and SIGBUS handlers sigaction (Signal.SIGBUS, sigbus, IntPtr.Zero); sigaction (Signal.SIGSEGV, sigsegv, IntPtr.Zero); Marshal.FreeHGlobal (sigbus); Marshal.FreeHGlobal (sigsegv); } static void EnableCrashReportingUnsafe () { // Run your crash reporting library initialization code here-- // this example uses HockeyApp but it should work well // with TestFlight or other libraries. // Verify in documentation that your library of choice // installs its sigaction hooks before leaving this method. var manager = BITHockeyManager.SharedHockeyManager; manager.Configure (HockeyAppId, null); manager.StartManager (); }
在FinishedLaunching
方法的开始调用EnableCrashReporting ()
。
如果需要,可以在#if !DEBUG
指令中包装这个调用。
它是如何工作的?
我遵循罗尔夫的build议:
一种可能的解决scheme是允许mono处理所有的SIGSEGV信号(从技术上讲,崩溃报告lib应该不处理SIGSEGV信号,或者它应该链接到mono的处理程序,而不是自己做任何处理)。 如果单声道确定SIGSEGV信号不是来自托pipe代码(即发生了一些非常糟糕的事情),它会产生一个SIGABORT信号(崩溃报告库应该已经处理并将其视为崩溃)。 正如你所能理解的那样,这是崩溃报告库中必须要做的事情。
Landon Fuller的Objective C实现:
#import <signal.h> @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { /* Save Mono's signal handler actions */ struct sigaction sigbus_action, sigsegv_action; sigaction(SIGBUS, NULL, &sigbus_action); sigaction(SIGSEGV, NULL, &sigsegv_action); // Enable the crash reporter here. Ie, [[PLCrashReporter sharedReporter] enableCrashReporterAndReturnError:], // or whatever is the correct initialization mechanism for the crash reporting service you're using /* Restore Mono's signal handlers */ sigaction(SIGBUS, &sigbus_action, NULL); sigaction(SIGSEGV, &sigsegv_action, NULL); return YES; }
我使用Banshee源代码作为如何从MonoTouch调用sigaction
的参考点。
希望能帮助到你!
从Xamarin.iOS 10.4开始,现在有一个支持的方式来做到这一点:
static void EnableCrashReporting () { try { } finally { Mono.Runtime.RemoveSignalHandlers (); try { EnableCrashReportingUnsafe (); } finally { Mono.Runtime.InstallSignalHandlers (); } } } static void EnableCrashReportingUnsafe () { // Run your crash reporting library initialization code here-- // this example uses HockeyApp but it should work well // with TestFlight or other libraries. // Verify in documentation that your library of choice // installs its sigaction hooks before leaving this method. // Have in mind that at this point Mono will not handle // any NullReferenceExceptions, if there are any // NullReferenceExceptions on any thread (not just the current one), // then the app will crash. var manager = BITHockeyManager.SharedHockeyManager; manager.Configure (HockeyAppId, null); manager.StartManager (); }