iOS CoreBluetooth очередь отправки для фоновой обработки приложения - PullRequest
1 голос
/ 15 июня 2019

Прежде всего вопрос, каков наилучший способ использования ядра 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 очередь, котораяне был бы в потоке пользовательского интерфейса и не был бы закрыт, когда приложение переходит в фоновый режим, будет принята с благодарностью.Если это невозможно, мне нужно выбрать один из обходных путей.

...