@antaki93
Пишу под Android.

Почему BLE-коллбэки «перебивают» выполнение кода handler'а?

В своём приложении на Android я работаю с BLE. Делаю запросы к удалённому устройству и получаю от него асинхронные ответы в коллбэках.
В то же время, я использую handler.postDelayed(), который через заданное время закрывает соединение, если устройство не отвечает.
Выглядит это так
1) делаем запрос к устройству
2) если в течение 10 сек. получаем ответ, продолжаем работу
3) делаем новый запрос
4) если в течение 10 сек. ответа нет, закрываем соединение
5) завершаем работу

Так вот, на днях у меня последовательно сработали сначала handler, завершающий работу, а затем (через 36 миллисек.) пришёл ответ от устройства (который уже никто не ждал).
В итоге лог выглядел примерно так
1) сделали запрос
2) ответ не пришёл, закрываем соединение
3) ответ ПРИШЁЛ, пытаемся продолжать работу

Короче говоря, коллбэк выполнился, не дожидаясь завершения выполнения handler'а. Хотя, если я правильно понимаю, они оба должны выполняться в одном потоке и не могут "перебивать" друг друга. Кто-нибудь может объяснить, как это может работать?

Выдержки из исходника
Подключаемся к устройству:
poll = true;
deviceNumber = 0;
characteristicValues = new String[bleDevices.size()];
characteristicValues[deviceNumber] = "ERROR";   // в случае успеха "ERROR" заменится на значение характеристики
handler.postDelayed(responseWaiting, RESPONSE_TIMEOUT); //RESPONSE_TIMEOUT=10000
bleDevices.get(deviceNumber).connectGatt(appContext, false, bluetoothGattCallback);


Получаем статус подключения и обрабатываем его:
private BluetoothGattCallback bluetoothGattCallback = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
            handler.removeCallbacks(responseWaiting);
            currentGatt = gatt;
            switch (newState) {
                case BluetoothGatt.STATE_CONNECTED:
                    if (poll) {
                        if (status == BluetoothGatt.GATT_SUCCESS) {
                            handler.postDelayed(responseWaiting, RESPONSE_TIMEOUT);
                            gatt.discoverServices();
                        } else {
                            nextDevice();
                        }
                    } else {
                        handler.postDelayed(disconnectWaiting, RESPONSE_TIMEOUT);
                        currentGatt.disconnect();
                    }
                    break;
                case BluetoothGatt.STATE_DISCONNECTED:
                    if (poll) {
                        nextDevice();
                    } else {
                        currentGatt.close();
                    }
                    break;
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            //…
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            //…
        }
    };


Подключение к следующему устройству:
private void nextDevice() {
        currentGatt.close();
        deviceNumber++;
        if (deviceNumber < bleDevices.size()) {
            characteristicValues[deviceNumber] = "ERROR";   // в случае успеха "ERROR" заменится на значение характеристики
            handler.postDelayed(responseWaiting, RESPONSE_TIMEOUT);
            bleDevices.get(deviceNumber).connectGatt(appContext, false, bluetoothGattCallback);
        } else {
            fireListeners();
        }
    }


Завершение опроса и отправка результатов внешнему слушателю:
private void fireListeners() {
        for (BLEClientListener bleClientListener : bleClientListeners) {
            if (poll) {
                poll = false;
                bleClientListener.onValuesReceived(characteristicValues);
            }
        }
    }
  • Вопрос задан
  • 157 просмотров
Решения вопроса 1
@antaki93 Автор вопроса
Пишу под Android.
Потому что работа с BLE происходит в отдельном служебном потоке (система сама создаёт его, и это единственный верный подход для работы с любой сетью). И коллбэки, соответственно, тоже вызываются в другом потоке. Для взаимодействия с потоком UI можно использовать тот же Handler или BroadcastReceiver'ы. И использовать собственные флаги состояния, конечно (типа ETA_SHTUKA_CONNECTED, ETA_SHTUCA_WAITING).
Ответ написан
Комментировать
Пригласить эксперта
Ответы на вопрос 1
Nightmare1
@Nightmare1
Программист
Вероятно запрос возникает в отдельном потоке? Попробуйте ввести функцию таймаута например, а вообще желательно что бы вы показали код в котором это происходит, ибо на пальцах не понятно.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы