"UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method..." during app upgrade on Xamarin.iOS

"UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method..." during app upgrade on Xamarin.iOS

我们刚刚使用 Azure 通知中心作为提供者,为我们的 Xamarin.forms 应用实施了一项新的推送通知功能。在 iOS 上,当应用程序在 iOS 设备上全新安装时,通知工作正常,即设备上没有安装该应用程序的先前版本。但是当我尝试通过在顶部安装较新版本来升级 iOS 设备上的现有应用程序时,我没有收到任何通知。

我在调试的时候发现,只有在应用升级的时候,iOS应用才会抛出一个UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.' 由于此异常设备未注册 Apple 推送通知服务,因此未收到任何推送通知。

下面是堆栈跟踪的完整异常:

UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.'

UIKit.UIKitThreadAccessException
  Message=UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.
  Source=mscorlib
  StackTrace:
  at UIKit.UIApplication.EnsureUIThread () [0x00020] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIApplication.cs:96 
  at UIKit.UIGestureRecognizer..ctor (Foundation.NSObject target, System.IntPtr action) [0x00016] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIGestureRecognizer.g.cs:102 
  at UIKit.UIGestureRecognizer..ctor (System.IntPtr sel, UIKit.UIGestureRecognizer+Token token) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIGestureRecognizer.cs:66 
  at UIKit.UITapGestureRecognizer..ctor (System.Action`1[T] action) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/UIKit/UIGestureRecognizer.cs:208 
  at Xamarin.Forms.Platform.iOS.EventTracker.CreateTapRecognizer (System.Int32 numTaps, System.Action`1[T] action, System.Int32 numFingers) [0x00000] in D:\a\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:462 
  at Xamarin.Forms.Platform.iOS.EventTracker.GetNativeRecognizer (Xamarin.Forms.IGestureRecognizer recognizer) [0x00049] in D:\a\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:258 
  at Xamarin.Forms.Platform.iOS.EventTracker.LoadRecognizers () [0x00042] in D:\a\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:572 
  at Xamarin.Forms.Platform.iOS.EventTracker.ModelGestureRecognizersOnCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs notifyCollectionChangedEventArgs) [0x00000] in D:\a\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:624 
  at (wrapper delegate-invoke) <Module>.invoke_void_object_NotifyCollectionChangedEventArgs(object,System.Collections.Specialized.NotifyCollectionChangedEventArgs)
  at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00018] in <b912bfaf235d4ed8af62226c84967349>:0 
  at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedAction action, System.Object item, System.Int32 index) [0x00009] in <b912bfaf235d4ed8af62226c84967349>:0 
  at System.Collections.ObjectModel.ObservableCollection`1[T].InsertItem (System.Int32 index, T item) [0x0001a] in <b912bfaf235d4ed8af62226c84967349>:0 
  at System.Collections.ObjectModel.Collection`1[T].Add (T item) [0x00020] in <cbddc4225b2f45f09f3a1d43a1268bc0>:0 
  at Xamarin.Forms.View.<.ctor>g__AddItems|14_1 (Xamarin.Forms.View+<>c__DisplayClass14_0& ) [0x00032] in D:\a\s\Xamarin.Forms.Core\View.cs:85 
  at Xamarin.Forms.View.<.ctor>b__14_0 (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args) [0x0002f] in D:\a\s\Xamarin.Forms.Core\View.cs:101 
  at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00018] in <b912bfaf235d4ed8af62226c84967349>:0 
  at System.Collections.ObjectModel.ObservableCollection`1[T].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedAction action, System.Object item, System.Int32 index) [0x00009] in <b912bfaf235d4ed8af62226c84967349>:0 
  at System.Collections.ObjectModel.ObservableCollection`1[T].InsertItem (System.Int32 index, T item) [0x0001a] in <b912bfaf235d4ed8af62226c84967349>:0 
  at System.Collections.ObjectModel.Collection`1[T].Add (T item) [0x00020] in <cbddc4225b2f45f09f3a1d43a1268bc0>:0 
  at SWAPA.MemberMobileApp.UI.Views.Login.CommonSetup () [0x002a1] in C:\MemberMobileApp\SWAPA.MemberMobileApp.UI\SWAPA.MemberMobileApp.UI\SWAPA.MemberMobileApp.UI\Views\Login.xaml.cs:369 
  at System.Runtime.CompilerServices.AsyncMethodBuilderCore+<>c.<ThrowAsync>b__7_1 (System.Object state) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/runtime/compilerservices/AsyncMethodBuilder.cs:1037 
  at System.Threading.QueueUserWorkItemCallback.WaitCallback_Context (System.Object state) [0x0000d] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1370 
  at System.Threading.ExecutionContext.RunInternal (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00071] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:968 
  at System.Threading.ExecutionContext.Run (System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, System.Object state, System.Boolean preserveSyncCtx) [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/executioncontext.cs:910 
  at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem () [0x00021] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1341 
  at System.Threading.ThreadPoolWorkQueue.Dispatch () [0x00074] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:899 
  at ObjCRuntime.Runtime.ThreadPoolDispatcher (System.Func`1[TResult] callback) [0x00006] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.18.2.1/src/Xamarin.iOS/ObjCRuntime/Runtime.cs:288 
  at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback () [0x00009] in /Library/Frameworks/Xamarin.iOS.framework/Versions/Current/src/Xamarin.iOS/mcs/class/referencesource/mscorlib/system/threading/threadpool.cs:1258 

我在 AppDelegate 中完成的唯一实现是向 Apple 推送通知服务注册设备,然后将该设备注册 ID 发送到主 UI 应用程序以向 Azure 通知中心注册设备。

下面是我的代码和一些注释:

    [Register("AppDelegate")]
    public partial class AppDelegate : global::Xamarin.Forms.Platform.iOS.FormsApplicationDelegate, IUNUserNotificationCenterDelegate
    {
        //
        // This method is invoked when the application has loaded and is ready to run. In this 
        // method you should instantiate the window, load the UI into it and then make the window
        // visible.
        //
        // You have 17 seconds to return from this method, or iOS will terminate your application.
        //
        public override bool FinishedLaunching(UIApplication app, NSDictionary options)
        {
            global::Xamarin.Forms.Forms.Init();

            //----Block needed for App Center Testing-----
#if ENABLE_TEST_CLOUD
            //Xamarin.Calabash.Start();
#endif
            //----Block needed for App Center Testing-----

            new SfCalendarRenderer();

            LoadApplication(new App());
            SfImageEditorRenderer.Init();
            SfListViewRenderer.Init();
            SfCheckBoxRenderer.Init();
            SfPdfDocumentViewRenderer.Init();
            SfPickerRenderer.Init();
            Syncfusion.SfChart.XForms.iOS.Renderers.SfChartRenderer.Init();
            new Syncfusion.SfAutoComplete.XForms.iOS.SfAutoCompleteRenderer();

            UITabBar.Appearance.SelectedImageTintColor = UIColor.FromRGB(213, 84, 39);

            // Color of the tabbar background:
            UITabBar.Appearance.BarTintColor = UIColor.LightGray;

            // Color of the selected tab text color:
            UITabBarItem.Appearance.SetTitleTextAttributes(
                new UITextAttributes()
                {
                    TextColor = UIColor.FromRGB(213, 84, 39)
                },
                UIControlState.Selected);

            // Color of the unselected tab icon & text:
            UITabBarItem.Appearance.SetTitleTextAttributes(
                new UITextAttributes()
                {
                    TextColor = UIColor.FromRGB(65, 64, 66)
                },
                UIControlState.Normal);

            base.FinishedLaunching(app, options);

            /*Register device with Apple Push Notification Service*/
            RegisterForRemoteNotifications();

            if (options != null && options.ContainsKey(UIApplication.LaunchOptionsRemoteNotificationKey))
            {
                NSDictionary userInfo = (NSDictionary)options[UIApplication.LaunchOptionsRemoteNotificationKey];
                if (userInfo != null)
                {
                    PushNotifications.IsNotifictaionClick_AppInActive = true;
                    // To remove all delivered notifications
                    RemoveAllDeliveredPushNotifications();
                }
            }
            return true;
        }

        //Register device with Apple Push Notification Services
        void RegisterForRemoteNotifications()
        {
            //register for remote notifications based on system version
            if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
            {
                UNUserNotificationCenter.Current.RequestAuthorization(UNAuthorizationOptions.Alert |
                    UNAuthorizationOptions.Sound |
                    UNAuthorizationOptions.Sound,
                    (granted, error) =>
                    {
                        if (granted)
                            InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications); // Ask user for permission to receive notifications on device.
                    });
            }
            else if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
            {
                var pushSettings = UIUserNotificationSettings.GetSettingsForTypes(
                UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound,
                new NSSet());

                UIApplication.SharedApplication.RegisterUserNotificationSettings(pushSettings);
                UIApplication.SharedApplication.RegisterForRemoteNotifications();
            }
            else
            {
                UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge | UIRemoteNotificationType.Sound;
                UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
            }

            UNUserNotificationCenter.Current.Delegate = this;
        }

        //Send the PNS handle to UI app for device installation with Azure Notificaiton Hub.
        public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
        {
            //Format the pns handle before registering with Azure notification hub.
            PushNotifications.PNSHandle = deviceToken.DebugDescription.Replace("<", string.Empty)
                                                                      .Replace(">", string.Empty)
                                                                      .Replace(" ", string.Empty)
                                                                      .ToUpper();
        }

        // Process notification when received.
        public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
        {
            ProcessNotification(userInfo);
        }

        void ProcessNotification(NSDictionary options)
        {
            // make sure we have a payload
            if (options != null && options.ContainsKey(new NSString("aps")))
            {
                // get the APS dictionary and extract message payload. Message JSON will be converted
                // into a NSDictionary so more complex payloads may require more processing
                NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
                NSDictionary alertMessage = aps.ObjectForKey(new NSString("alert")) as NSDictionary;

                //Extract notification content
                NSString messageKey = new NSString("body");
                NSString titleKey = new NSString("title");

                string messageBody = alertMessage.ContainsKey(messageKey) ? alertMessage[titleKey].ToString() : string.Empty;
                string title = alertMessage.ContainsKey(titleKey) ? alertMessage[titleKey].ToString() : string.Empty;

                if (!string.IsNullOrWhiteSpace(title) || !string.IsNullOrWhiteSpace(messageBody))
                {
                    var content = new UNMutableNotificationContent();
                    content.Title = title;
                    content.Body = messageBody;

                    var requestID = title;
                    var request = UNNotificationRequest.FromIdentifier(requestID, content, null);

                    UNUserNotificationCenter.Current.AddNotificationRequest(request, (err) =>
                    {
                        if (err != null)
                        {
                            // TODO: log error messages in error data table.
                            Debug.WriteLine($"Received request to process notification but something went wrong.");
                        }
                    });
                }
            }
            else
            {
                // TODO: log error messages in error data table.
                Debug.WriteLine($"Received request to process notification but there was no payload.");
            }
        }

        [Export("userNotificationCenter:willPresentNotification:withCompletionHandler:")]
        public void WillPresentNotification(UNUserNotificationCenter center, UNNotification notification, Action<UNNotificationPresentationOptions> completionHandler)
        {
            completionHandler(UNNotificationPresentationOptions.Sound | UNNotificationPresentationOptions.Alert);
        }

        [Export("userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:")]
        public void DidReceiveNotificationResponse(UNUserNotificationCenter center, UNNotificationResponse response, Action
        completionHandler)
        {
            completionHandler();

            /*Logic to open NotificationInboxPage onclick of notification when app was active */
            if (!App.IsAppLaunching)
            {
                var modalStack = App.Current.MainPage.Navigation.ModalStack;

                if (modalStack.Count > 0 && modalStack.Last().ToString().Contains("NotificationInbox"))
                    App.Current.MainPage.Navigation.PopModalAsync();

                App.Current.MainPage.Navigation.PushModalAsync(new NotificationInboxPage());
            }
            App.IsAppLaunching = false;

            //To remove all delivered notifications
            RemoveAllDeliveredPushNotifications();
        }

        private void RemoveAllDeliveredPushNotifications()
        {
            if (UIDevice.CurrentDevice.CheckSystemVersion(10, 0))
            {
                UNUserNotificationCenter.Current.RemoveAllDeliveredNotifications();
            }
            else
            {
                UIApplication.SharedApplication.CancelAllLocalNotifications();
            }
        }
}

异常显示我正在调用 UIKit 方法,该方法只能从 UI 线程调用。我调用的唯一操作是 InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications); 我已经在主线程上调用了。所以我不确定异常在谈论什么其他功能。为什么它只在我升级应用程序时抱怨,而不是在我刚安装应用程序时抱怨。

我检查了这个 post here 但没有得到太多帮助。在这一点上,我完全被困住了,真的需要一些帮助来解决这个问题。知道我可能遗漏了什么吗?

提前致谢。

从问题中提供的堆栈跟踪中可以看出,错误实际上发生在“Login.xaml.cs”文件的第 369 行。

如果您放置一个“异常捕获点”,它应该会准确告诉您是哪一行导致了该问题。在它周围放置一个 try-catch 应该可以解决崩溃问题,但也要尝试解决这个问题,因为一般来说异常是不好的。

除此之外,您还可以改用 DispatchAsync