Прежде всего вопрос, каков наилучший способ использования ядра Bluetooth в центральной роли для отправки данных для отправки данных на устройства Bluetooth LE. Данные, которые он отправляет, должны быть обработаны, и это занимает достаточно времени, чтобы вызвать проблемы в потоке пользовательского интерфейса, если он выполняется на нем. Пользователь запустит процесс с открытым приложением телефона, а затем либо продолжит использовать приложение, либо закроет приложение и ожидает, что данные продолжат отправку на устройство.
Я нашел 2 действительно плохих способа сделать это, которые, кажется, работают
- Поместите объект Bluetooth CBCentralManager в основную очередь и рискуйте заблокировать пользовательский интерфейс
- Игнорируйте указания от стека Bluetooth для iOS, что он не готов к передаче, и рискуете потерять данные.
Похоже, это имеет свои корни как в очередях потоковой передачи / отправки iOS, так и во внутренних устройствах iOS Bluetooth.
Приложение Bluetooth LE iOS подключается к устройству Bluetooth LE в качестве центральной роли. CBCentralManager инициализируется в соответствии с яблочной документацией . Очередь определена как:
Очередь отправки, используемая для отправки событий центральной роли. Если
значение равно нулю, центральный менеджер отправляет события центральной роли, используя
основная очередь.
Как подсказывает vladiulianbogdan ответ Swift: выберите очередь для Bluetooth Central manager мы должны создать последовательную очередь для CBCentralManager. Кажется, это имеет смысл, и какое-то время я следовал этому совету. Также allprog комментарий к Swift CoreBluetooth: должен ли CentralManager работать в отдельном потоке
предполагает, что основная очередь будет приостановлена, а другие очереди - нет, что противоположно тому, что я вижу.
При использовании последовательной очереди для Bluetooth предпочтительнее использовать другую очередь, нежели основной поток. Возникла проблема: Обратный звонок:
-(void)peripheralIsReadyToSendWriteWithoutResponse:(CBPeripheral *)peripheral
{
[self sendNextBluetoothLePacket];
}
прекрати звонить. Есть еще один способ проверить, готово ли периферийное устройство для отправки дополнительных данных, CBPeripheral имеет переменную-член canSendWriteWithoutResponse , которая возвращает true, если отправка в порядке. Эта переменная также начинает возвращать значение false и никогда не возвращается к значению true.
Я нашел этот комментарий от Sandeep Bhandari , в котором говорится, что все потоки очереди останавливаются, когда приложение переходит в фоновый режим, если только они не являются одним из фоновых режимов, предоставляемых Apple. Биниу обнаружил, что ему удалось решить проблему с основным фоном Bluetooth, инициализировав его в контроллере представления вместо делегата приложения . Это не имеет смысла для меня.
Мое приложение имеет фоновый режим core-bluetooth, выбранный в его info.plist, поэтому оно должно быть одним из этих фоновых режимов. Я обнаружил, что когда мое приложение работает в фоновом режиме, оно продолжает обрабатывать данные. Я вижу сообщения журнала от циклов опроса, которые запускаются каждые 100 миллисекунд.
Если я запускаю записи bluetooth LE из этих циклов опроса, я могу продолжать отправлять данные. Проблема в том, что я не могу определить безопасную скорость отправки данных, и она либо очень медленная, либо данные иногда теряются.
Я не уверен, как лучше всего с этим справиться. Мы ценим любые предложения. Кажется, что независимо от того, что я делаю, когда я вхожу в фоновый режим, я теряю способность определять, безопасно ли отправлять данные.
Я вижу этот комментарий , который указывает, что единственным решением было бы изменить способ подключения устройства Bluetooth к телефону, это правда? На данный момент я не уверен, что мне нужно менять аппаратное обеспечение.
Идеальным решением было бы найти способ поместить CBCentralManager в свою собственную последовательную очередь, но создать эту очередь таким образом, чтобы очередь не была остановлена, когда приложение переходит в фоновый режим. Если кто-то знает, как это сделать, я верю, что это решит мою проблему.
То, как мой текущий код выглядит следующим образом.Когда служба bluetooth создается в обратном вызове applicationDidFinishLaunchingWithOptions к моему AppDelegate
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 очередь, котораяне был бы в потоке пользовательского интерфейса и не был бы закрыт, когда приложение переходит в фоновый режим, будет принята с благодарностью.Если это невозможно, мне нужно выбрать один из обходных путей.