【问题标题】:Bluetooth Scan throwing ArgumentOutOfRangeException in Xamarin.iOS蓝牙扫描在 Xamarin.iOS 中抛出 ArgumentOutOfRangeException
【发布时间】:2020-05-12 17:38:08
【问题描述】:

我有一个 BLE 扫描应用程序,它使用 this plugin 持续扫描附近的设备。发现设备后,会将它们添加到 ObservableRangeCollection(来自 James Montemagno 的 MvvmHelpers)。如果设备已经在列表中并且它的一些数据(例如,RSSI 强度)发生了变化,则使用ReplaceRange() 更新集合。如果设备在列表中的时间过长而没有更新,则将其从集合中删除。

这在 Android 上运行顺利,但在 iOS 上会引发 ArgumentOutOfRange 异常。异常并不总是同时出现,但如果我让应用程序运行足够长的时间,它最终会抛出。不幸的是,堆栈跟踪没有指向我的代码中的任何内容,因此很难判断这是否与 BLE 插件、ObservableRangeCollection 插件甚至 Xamarin.Forms 相关。

这是异常消息和堆栈跟踪:

{System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.Parameter name: index
  at Xamarin.Forms.ListProxy.get_Item (System.Int32 index) [0x0000b] in D:\a\1\s\Xamarin.Forms.Core\ListProxy.cs:129 
  at Xamarin.Forms.ListProxy.System.Collections.IList.get_Item (System.Int32 index) [0x00000] in D:\a\1\s\Xamarin.Forms.Core\ListProxy.cs:443 
  at Xamarin.Forms.Internals.TemplatedItemsList`2[TView,TItem].get_Item (System.Int32 index) [0x00008] in <7d3a3d515f644268b15520ab8aaf1bfc>:0 
  at Xamarin.Forms.Platform.iOS.ListViewRenderer+ListViewDataSource.GetCellForPath (Foundation.NSIndexPath indexPath) [0x00007] in D:\a\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:1327 
  at Xamarin.Forms.Platform.iOS.ListViewRenderer+ListViewDataSource.GetCell (UIKit.UITableView tableView, Foundation.NSIndexPath indexPath) [0x00021] in D:\a\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:1036 
  at (wrapper managed-to-native) ObjCRuntime.Messaging.void_objc_msgSend(intptr,intptr)
  at UIKit.UITableView.EndUpdates () [0x0000d] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.16.0.13/src/Xamarin.iOS/UITableView.g.cs:294 
  at Xamarin.Forms.Platform.iOS.ListViewRenderer+<>c__DisplayClass52_0.<DeleteRows>b__0 () [0x00048] in D:\a\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:599 
  at Xamarin.Forms.Platform.iOS.ListViewRenderer.DeleteRows (System.Int32 oldStartingIndex, System.Int32 oldItemsCount, System.Int32 section) [0x00046] in D:\a\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:603 
  at Xamarin.Forms.Platform.iOS.ListViewRenderer.UpdateItems (System.Collections.Specialized.NotifyCollectionChangedEventArgs e, System.Int32 section, System.Boolean resetWhenGrouped) [0x00119] in D:\a\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:542 
  at Xamarin.Forms.Platform.iOS.ListViewRenderer.OnCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x00000] in D:\a\1\s\Xamarin.Forms.Platform.iOS\Renderers\ListViewRenderer.cs:332 
  at Xamarin.Forms.Internals.TemplatedItemsList`2[TView,TItem].OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x0000a] in <7d3a3d515f644268b15520ab8aaf1bfc>:0 
  at Xamarin.Forms.Internals.TemplatedItemsList`2[TView,TItem].OnProxyCollectionChanged (System.Object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x0045f] in <7d3a3d515f644268b15520ab8aaf1bfc>:0 
  at Xamarin.Forms.ListProxy.OnCollectionChanged (System.Collections.Specialized.NotifyCollectionChangedEventArgs e) [0x0000a] in D:\a\1\s\Xamarin.Forms.Core\ListProxy.cs:232 
  at (wrapper other) System.Object.gsharedvt_out_sig(object&,intptr)
  at Xamarin.Forms.ListProxy+<>c__DisplayClass34_0.<OnCollectionChanged>b__0 () [0x00018] in D:\a\1\s\Xamarin.Forms.Core\ListProxy.cs:208 
  at (wrapper other) System.Object.__interp_lmf_mono_interp_entry_from_trampoline(intptr,intptr)
  at Foundation.NSAsyncActionDispatcher.Apply () [0x00000] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.16.0.13/src/Xamarin.iOS/Foundation/NSAction.cs:152 
  at (wrapper managed-to-native) UIKit.UIApplication.UIApplicationMain(int,string[],intptr,intptr)
  at UIKit.UIApplication.Main (System.String[] args, System.IntPtr principal, System.IntPtr delegate) [0x00005] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.16.0.13/src/Xamarin.iOS/UIKit/UIApplication.cs:86 
  at UIKit.UIApplication.Main (System.String[] args, System.String principalClassName, System.String delegateClassName) [0x0000e] in /Library/Frameworks/Xamarin.iOS.framework/Versions/13.16.0.13/src/Xamarin.iOS/UIKit/UIApplication.cs:65 
  at SDA.iOS.Application.Main (System.String[] args) [0x00001] in C:\Users\shodg\SDA\SDA\SDA.iOS\Main.cs:12 

这是视图模型中用于从 ObservableRangeCollection 添加/更新/删除设备的相关代码:

        public ObservableRangeCollection<DeviceListItemViewModel> Devices { get; set; } = new ObservableRangeCollection<DeviceListItemViewModel>();

        private void OnDeviceAdded(BleDevice device)
        {
            Devices.Add(new DeviceListItemViewModel(device));
        }

        private void OnDeviceUpdated(BleDevice device)
        {
            Devices.ReplaceRange(Devices.OrderByDescending(x => x.Rssi).ToList());
        }

        private void OnDeviceExpired(BleDevice device)
        {
            var vm = Devices.FirstOrDefault(d => d.Id == device.Id);
            Devices.Remove(vm);
        }

似乎异常与 iOS 上的 ListView 更新有关,我在 problems with ObservableCollections in Xamarin.iOS 上看到了一些线程,但我无法确定这个问题是出在 Montemagno 插件还是其他地方,以及是什么最好的前进道路是。

欢迎任何见解!

【问题讨论】:

  • 删除设备时似乎发生了异常。您可以自行调试以查看删除设备时发生的情况。到目前为止,您分享的代码看起来不错。

标签: c# xamarin xamarin.ios observablecollection ios-bluetooth


【解决方案1】:

事实证明,持续运行的 BLE 扫描会产生竞争条件,即在使用已删除设备更新列表的同时删除项目。

在主线程上调用并向属性添加锁似乎可以防止抛出异常:

        private object _deviceListLock = new object();
        public ObservableRangeCollection<DeviceListItemViewModel> Devices { get; set; } = new ObservableRangeCollection<DeviceListItemViewModel>();

        private void OnDeviceAdded(BleDevice device)
        {
            InvokeOnMainThread(() =>
            {
                lock (_deviceListLock)
                {
                    Devices.Add(new DeviceListItemViewModel(device));
                }
            });
        }

        private void OnDeviceUpdated(BleDevice device)
        {
            InvokeOnMainThread(() =>
            {
                lock (_deviceListLock)
                {
                    Devices.ReplaceRange(Devices.OrderByDescending(x => x.Rssi).ToList());
                }
            });
        }

        private void OnDeviceExpired(BleDevice device)
        {
            InvokeOnMainThread(() =>
            {
                lock (_deviceListLock)
                {
                    var vm = Devices.FirstOrDefault(d => d.Id == device.Id);
                    Devices.Remove(vm);
                }
            });
        }

为什么这在 iOS 上是必要的,但在 Android 上不是,我不完全确定。

【讨论】:

  • 能否请您标记此答案,以便帮助更多有相同问题的人:)。
猜你喜欢
  • 2015-03-03
  • 1970-01-01
  • 2018-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-26
相关资源
最近更新 更多