简体   繁体   English

“UIKit.UIKitThreadAccessException:'UIKit 一致性错误:您正在调用 UIKit 方法......”在 Xamarin.iOS 上的应用程序升级期间

[英]“UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method…” during app upgrade on Xamarin.iOS

We just implemented a new push notification feature for our Xamarin.forms app using azure notification hub as provider.我们刚刚使用 azure 通知中心作为提供者,为我们的 Xamarin.forms 应用程序实现了新的推送通知功能。 On iOS the notifications work perfectly fine when app is fresh installed on iOS device, ie no prior version of the app is installed on device.在 iOS 上,当应用程序在 iOS 设备上全新安装时,通知工作得非常好,即设备上没有安装应用程序的早期版本。 But when I try to upgrade the existing app on iOS device by installing the newer version on top, then I don't get any notifications.但是,当我尝试通过在顶部安装较新版本来升级 iOS 设备上的现有应用程序时,我没有收到任何通知。

When I debugged, I found that only during app upgrade scenario, the iOS app throws a UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit method that can only be invoked from the UI thread.'当我调试时,我发现只有在应用程序升级的情况下,iOS 应用程序会抛出UIKit.UIKitThreadAccessException: 'UIKit Consistency error: you are calling a UIKit' method that can only be invoked from the thread. Due to this exception the device doesn't get registered with Apple Push Notification Services and thus doesn't get any push notifications.由于此异常,设备未注册 Apple 推送通知服务,因此不会收到任何推送通知。

Below is the full exception with stacktrace:以下是堆栈跟踪的完整异常:

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\1\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:462 
  at Xamarin.Forms.Platform.iOS.EventTracker.GetNativeRecognizer (Xamarin.Forms.IGestureRecognizer recognizer) [0x00049] in D:\a\1\s\Xamarin.Forms.Platform.iOS\EventTracker.cs:258 
  at Xamarin.Forms.Platform.iOS.EventTracker.LoadRecognizers () [0x00042] in D:\a\1\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\1\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\1\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\1\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 

The only implementation that I have done in AppDelegate is to register the device with Apple Push Notification Services and then send that device registration id to main UI app to register the device with Azure notification hub.我在 AppDelegate 中完成的唯一实现是向 Apple 推送通知服务注册设备,然后将该设备注册 ID 发送到主 UI 应用程序以向 Azure 通知中心注册设备。

Below is my code with some comments:下面是我的代码和一些评论:

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

The exception says I am calling UIKit method that should only be invoked from UI thread.异常说我正在调用 UIKit 方法,该方法只能从 UI 线程调用。 The only action I am calling is InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications);我调用的唯一操作是InvokeOnMainThread(UIApplication.SharedApplication.RegisterForRemoteNotifications); which I am already invoking on main thread.我已经在主线程上调用了它。 So I am not sure what other function is the exception talking about.所以我不确定其他 function 是什么例外。 And why is it complaining only when I am upgrading the app and not when I am fresh installing the app.为什么它只在我升级应用程序时才抱怨,而不是在我刚安装应用程序时抱怨。

I checked this post here but didn't get much help.在这里查看了这篇文章,但没有得到太多帮助。 At this point I am completely stuck due to this and really need some help on resolving the issue.在这一点上,我完全被困住了,真的需要一些帮助来解决这个问题。 Any idea what I might be missing?知道我可能会错过什么吗?

Thanks in advance.提前致谢。

As you can read from the stack trace provided by you in the questions, the error is actually occuring on line 369 of your 'Login.xaml.cs' file.正如您在问题中提供的堆栈跟踪中所读到的那样,错误实际上发生在您的“Login.xaml.cs”文件的第 369 行。

If you place an "Exception catchpoint" and it should tell you exactly what line is causing that issue.如果您放置一个“异常捕获点”,它应该准确地告诉您是哪一行导致了该问题。 Placing a try-catch around it should fix the crash, but try to to resolve the issue as well since exceptions in general are bad.在它周围放置一个 try-catch 应该可以修复崩溃,但也应该尝试解决这个问题,因为通常异常是不好的。

Besides that, you can also to use DispatchAsync instead除此之外,您还可以改用 DispatchAsync

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM