首先,在核心角色中使用核心蓝牙发送数据以将数据发送到蓝牙LE设备的最佳方法是什么。它发送的数据需要进行处理,并且如果在其上运行,则需要花费足够的时间在UI线程上引起问题。用户将在打开手机应用程序的情况下启动该过程,然后继续使用该应用程序或关闭该应用程序,并期望数据继续发送到设备。
我发现2种非常糟糕的方法似乎奏效了
这似乎起源于iOS线程/调度队列以及iOS Bluetooth内部。
蓝牙LE iOS应用程序作为中心角色连接到蓝牙LE设备。 CBCentralManager根据apples documentation初始化。队列定义为:
用于调度中央角色事件的调度队列。如果 值为零,中央管理器使用以下命令调度中央角色事件 主队列。
根据vladiulianbogdan answer to Swift: Choose queue for Bluetooth Central manager的建议,我们应该为CBCentralManager创建一个串行队列。这似乎很有意义,有一段时间我一直在遵循这个建议。另外,allprog comment to Swift CoreBluetooth: Should CentralManager run in a separate thread 建议将主队列挂起,而其他队列则不会挂起,这与我所看到的相反。
虽然将串行队列用于蓝牙,但使用与主线程不同的蓝牙队列则更可取。有一个问题:回调:
-(void)peripheralIsReadyToSendWriteWithoutResponse:(CBPeripheral *)peripheral
{
[self sendNextBluetoothLePacket];
}
别再打电话了。还有一种检查外围设备是否准备好发送更多数据的方法,CBPeripheral has a member variable canSendWriteWithoutResponse如果可以发送,则返回true。此变量还将开始重新调整为false,并且永远不会恢复为true。
我从Sandeep Bhandari中发现了一条评论,该评论说,当应用程序在后台运行时,所有队列线程都将停止,除非它们是apple提供的后台模式之一。 Biniou found that he was able to solve his core bluetooth background issue by initializing in a view controller instead of the app delegate。这对我来说没有意义。
我的应用程序确实在其info.plist中选择了核心蓝牙背景模式,因此它应该作为这些背景模式之一。我发现,当我的应用程序进入后台时,该应用程序确实会继续处理数据。我看到每100毫秒运行一次的轮询循环中的日志消息。
如果我从这些轮询循环中触发了蓝牙LE写入,则能够继续发送数据。问题是我无法确定发送数据的安全速率,而且传输速度非常慢或有时会丢失。
我不确定如何最好地处理此问题。任何建议,将不胜感激。似乎无论进入后台时做什么,我都会失去确定数据发送是否安全的能力。
我看到this comment表示唯一的解决方案是更改蓝牙设备与电话的连接方式,是这种情况吗?我不确定目前是否可以更改硬件。
理想的解决方案是找到一种方法,将CBCentralManager放在其自己的串行队列中,但以使该应用程序进入后台时该队列不会停止的方式创建该队列。如果有人知道该怎么做,我相信它将解决我的问题。
我当前的代码是这样的。在我的AppDelegate的applicationDidFinishLaunchingWithOptions回调中创建蓝牙服务后
self.cm = [[CBCentralManager alloc] initWithDelegate:self
queue:nil
options:@{CBCentralManagerOptionShowPowerAlertKey:@(0)}];
或者使用应该可以但不能使用的串行队列
dispatch_queue_t bt_queue = dispatch_queue_create("BT_queue", 0);
self.cm = [[CBCentralManager alloc] initWithDelegate:self
queue:bt_queue
options:@{CBCentralManagerOptionShowPowerAlertKey:@(0)}];
当需要发送一些数据时,我会使用其中之一
[self.cbPeripheral writeValue:data
forCharacteristic:self.rxCharacteristic
type:CBCharacteristicWriteWithoutResponse];
如果我一直不间断地调用此writeValue,而没有尝试检查其是否可以安全发送数据。最终将失败。
使用此代码建立连接后,需要额外的后台执行时间
- (void) centralManager:(CBCentralManager *)central
didConnectPeripheral:(CBPeripheral *)peripheral
{
UIApplication *app = [UIApplication sharedApplication];
if (self.globalBackgroundTask != UIBackgroundTaskInvalid) {
[app endBackgroundTask:self.globalBackgroundTask];
self.globalBackgroundTask = UIBackgroundTaskInvalid;
}
self.globalBackgroundTask = [app beginBackgroundTaskWithExpirationHandler:^{
[app endBackgroundTask:_globalBackgroundTask];
_globalBackgroundTask = UIBackgroundTaskInvalid;
}];
任何对此的想法或建议,如我如何给CBCentralManager一个不在UI线程上并且当应用程序进入后台时都不会关闭的队列,将不胜感激。如果那不可能,我需要尝试选择一种解决方法。