Xamarin iOS 蓝牙外围设备扫描永远不会看到任何外围设备

问题描述

我正在尝试创建一个可以在 iOS 和 Android 上运行的 Xamarin.Forms 应用。最终我需要应用程序的实例通过蓝牙相互通信,但我坚持让 iOS 端使用蓝牙做任何事情。我最初尝试使用 plugin.bluetoothlePlugin.BLE,但一周半后我无法获得广告或扫描以在任一操作系统上工作任何一个插件,所以我决定尝试使用平台 API 的 .NET 包装器来实现简单的蓝牙交互,这至少是有据可查的。我确实让扫描在 Android 端正常工作。但是,使用 iOS 时,我现在所拥有的构建得很好,并且在我的 iPad 上运行时没有错误,但是从不调用 discoveredPeripheral 处理程序,即使 iPad 距离 Android 平板电脑只有几英寸并且大概应该能够看到相同的设备。我已经通过在该方法中设置断点来验证这一点,该断点从未达到;当我在 iPad 上打开蓝牙设置使其可被发现时,Android 平板电脑上的应用程序版本可以看到它,所以我认为这不是 iPad 硬件问题。

很明显,我不知道该流程的某些部分要做什么,但(对我而言)还不清楚在哪里可以找到它。下面是与 CBCentralManager 交互的类的代码(据我了解,这应该包括返回外围设备列表所需的一切):

using MyBluetoothApp.Shared; // for the interfaces and constants
using CoreBluetooth;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Xamarin.Forms;

[assembly: Dependency(typeof(MyBluetoothApp.iOS.peripheralscanner))]
namespace MyBluetoothApp.iOS
{
    public class peripheralscanner : Iperipheralscanner
    {
        private readonly CBCentralManager manager;
        private List<IPeripheral> foundperipherals;

        public peripheralscanner()
        {
            this.foundperipherals = new List<IPeripheral>();

            this.manager = new CBCentralManager();
            this.manager.discoveredPeripheral += this.discoveredPeripheral;
            this.manager.UpdatedState += this.UpdatedState;
        }

        public async Task<List<IPeripheral>> ScanForService(string serviceUuid)
        {
            return await this.ScanForService(serviceUuid,BluetoothConstants.DEFAULT_SCAN_TIMEOUT);
        }

        public async Task<List<IPeripheral>> ScanForService(string serviceUuid,int duration)
        {
            CBUUID uuid = CBUUID.FromString(serviceUuid);
            //this.manager.ScanForperipherals(uuid);
            this.manager.ScanForperipherals((CBUUID)null); // For Now I'd be happy to see ANY peripherals

            await Task.Delay(duration);
            this.manager.StopScan();

            return this.foundperipherals;
        }

        private void discoveredPeripheral(object sender,CBdiscoveredPeripheralEventArgs args)
        {
            this.foundperipherals.Add(new CPeripheral(args.Peripheral));
        }

        private void UpdatedState(object sender,EventArgs args)
        {
            CBCentralManagerState state = ((CBCentralManager)sender).State;
            if (CBCentralManagerState.PoweredOn != state)
            {
                throw new Exception(state.ToString());
            }
        }
    }
}

谁能指出我理解我所缺少的东西的方向?

编辑:好的...K,我很偶然地发现,如果我在共享代码中这样做:

Iperipheralscanner scanner = DependencyService.Get<Iperipheralscanner>();
List<IPeripheral> foundperipherals = await scanner.ScanForService(BluetoothConstants.VITL_SERVICE_UUID);

连续两次,它第二次起作用。我感到更加充满希望,也更加困惑。

解决方法

潜在的问题是,在 PeripheralScanner 的第一次实例化中,ScanForService 在 State 更新之前被调用。我尝试了多种方法来等待引发该事件,以便确定状态为 PoweredOn,但似乎没有任何效果;轮询循环根本没有达到所需的状态,但是如果我在 UpdatedState 处理程序中抛出异常,它会在启动后的毫秒内抛出,并且当时的状态始终是 PoweredOn。 (该处理程序中的断点导致调试冻结,输出已解决挂起的断点,甚至 VS 团队似乎都无法解释)。

阅读一些 Apple 开发人员博客后,我发现通过在 UpdatedState 处理程序中执行所需的操作,通常可以避免这种情况。它终于浸入了我的头脑,我从未看到该处理程序运行产生任何影响,因为该事件是在不同的线程上引发和处理的。我真的需要将服务 UUID 传递给扫描逻辑,并与我可以从 ScanForService 返回的通用列表进行交互,因此将其全部移至处理程序似乎不是一个有希望的方向.所以我创建了一个单例来标记状态:

internal sealed class ManagerState // .NET makes singletons easy - Lazy<T> FTW
{
    private static readonly Lazy<ManagerState> lazy = new Lazy<ManagerState>(() => new ManagerState());
    internal static ManagerState Instance { get { return ManagerState.lazy.Value; } }
    internal bool IsPoweredOn { get; set; }

    private ManagerState()
    {
        this.IsPoweredOn = false;
    }
}

并在处理程序中更新它:

private void updatedState(object sender,EventArgs args)
{
    ManagerState.Instance.IsPoweredOn = CBCentralManagerState.PoweredOn == ((CBCentralManager) sender).State;
}

然后在 ScanForService 开始时轮询(每次在一个单独的线程中,因为同样,我不会在我的基本线程中看到更新):

    while (false == await Task.Run(() => ManagerState.Instance.IsPoweredOn)) { }

我完全不确定这是最好的解决方案,但它确实有效,至少在我的情况下是这样。我想我可以将逻辑移到处理程序并创建一个更高级的单例类来来回移动所有状态,但这对我来说感觉不太好。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...