蓝牙扫描在Xamarin.iOS中抛出ArgumentOutOfRangeException异常

问题描述 投票:0回答:1

我有一个BLE扫描应用程序,使用 此插件 来持续扫描附近的设备。当发现设备时,它们会被添加到一个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更新有关,我在网上看到了一些线程 Xamarin.iOS中ObservableCollections的问题。但我很难确定这个问题是在Montemagno插件还是其他地方,以及什么是最好的前进道路。

任何见解将是最欢迎的!

c# xamarin xamarin.ios observablecollection ios-bluetooth
1个回答
0
投票

因此,事实证明,连续运行的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上不需要,我不太清楚。

© www.soinside.com 2019 - 2024. All rights reserved.