Уменьшено BLE startScan обнаруженных устройств на Android 5.0 Lollipop

Укороченная версия:

В моих тестах с Android 5.0 Lollipop я заметил android.bluetooth.le.BluetoothLeScanner обнаруживает устройства BLE менее часто, чем Android 4.4 KitKat. Почему это и есть ли альтернатива?

Длинная версия:

Я разрабатываю приложение для Android, специально для планшета Nexus 7, которое предназначено для обнаружения устройств Bluetooth с низким энергопотреблением (BLE). Приложение в основном заинтересовано в значении RSSI маяков, чтобы определить их близость к планшету. Это означает, что мне не нужно подключаться к устройству BLE, поскольку значение RSSI передается обратному вызову сканирования при обнаружении устройства.

В Android 4.4 KitKat, когда я звоню BluetoothAdapter.startLeScan(LeScanCallback)мой обратный вызов вызывается только ОДИН РАЗ для каждого обнаруженного устройства BLE. (Я видел, что некоторые обсуждения утверждают, что это поведение может отличаться для разных устройств). Однако меня интересует постоянно меняющееся значение RSSI, поэтому в настоящее время рекомендуется постоянно запускать startLeScan и stopLeScan с заданным интервалом (в моем случае 250 мс):

public class TheOldWay {

    private static final int SCAN_INTERVAL_MS = 250;

    private Handler scanHandler = new Handler();
    private boolean isScanning = false;

    public void beginScanning() {
        scanHandler.post(scanRunnable);
    }

    private Runnable scanRunnable = new Runnable() {
        @Override
        public void run() {
            BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();

            if (isScanning) {
                adapter.stopLeScan(leScanCallback);
            } else if (!adapter.startLeScan(leScanCallback)) {
                // an error occurred during startLeScan
            }

            isScanning = !isScanning;

            scanHandler.postDelayed(this, SCAN_INTERVAL_MS);
        }
    };

    private BluetoothAdapter.LeScanCallback leScanCallback = new BluetoothAdapter.LeScanCallback() {
        @Override
        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
            // use the RSSI value
        }
    };

}

По сути, это дает мне требуемые результаты, но этот процесс очень ресурсоемкий и в конечном итоге приводит к не отвечающему адаптеру Bluetooth.

По этим причинам я обновил свой Nexus 7 до Android 5.0 Lollipop, чтобы посмотреть, будут ли исправлены проблемы с BLE. В Lollipop BluetoothAdapter.startLeScan(LeScanCallback) устарел и заменен новым API, который позволяет еще больше контролировать процесс сканирования. Из моих первых тестов кажется, что startScan не вызывает непрерывный вызов моего обратного вызова (на моем Nexus 7) при изменении значений RSSI, поэтому мне все еще нужно использовать реализацию startScan / stopScan:

@TargetApi(21)
public class TheNewWay {

    private static final int SCAN_INTERVAL_MS = 250;

    private Handler scanHandler = new Handler();
    private List<ScanFilter> scanFilters = new ArrayList<ScanFilter>();
    private ScanSettings scanSettings;
    private boolean isScanning = false;

    public void beginScanning() {
        ScanSettings.Builder scanSettingsBuilder = new ScanSettings.Builder();
        scanSettingsBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
        scanSettings = scanSettingsBuilder.build();

        scanHandler.post(scanRunnable);
    }

    private Runnable scanRunnable = new Runnable() {
        @Override
        public void run() {
            BluetoothLeScanner scanner = BluetoothAdapter.getDefaultAdapter().getBluetoothLeScanner();

            if (isScanning) {
                scanner.stopScan(scanCallback);
            } else {
                scanner.startScan(scanFilters, scanSettings, scanCallback);
            }

            isScanning = !isScanning;

            scanHandler.postDelayed(this, SCAN_INTERVAL_MS);
        }
    };

    private ScanCallback scanCallback = new ScanCallback() {
        @Override
        public void onScanResult(int callbackType, ScanResult result) {
            super.onScanResult(callbackType, result);

            int rssi = result.getRssi();

            // do something with RSSI value
        }

        @Override
        public void onScanFailed(int errorCode) {
            super.onScanFailed(errorCode);

            // a scan error occurred
        }
    };

}

Как видите, я настроил сканер с помощью класса ScanSettings, который позволяет вам установить scanMode, я использую ScanSettings.SCAN_MODE_LOW_LATENCY, который имеет следующую документацию: "Сканирование с использованием наибольшего коэффициента заполнения. Рекомендуется использовать этот режим, только когда приложение работает на переднем плане". Звучит точно так, как я хочу, но, к сожалению, я получаю сигнал маяка только каждые 15 - 30 секунд, когда версия KitKat показывает мне один и тот же сигнал маяка каждые 1 - 2 секунды на этом интервале сканирования.

У вас есть идеи, что может быть причиной этой разницы? Я что-то упустил, может быть, какие-то новые настройки? Есть ли альтернативные способы сделать выше?

Заранее большое спасибо!

Абель

PS: я хотел бы добавить больше ссылок на ресурсы, которые я использовал, но у меня пока нет точек повторения.

5 ответов

Решение

Я получил совсем другие результаты с Nexus 5, использующим новые API-интерфейсы сканирования Android 5.0. При использовании SCAN_MODE_LOW_LATENCY обнаружение пакетов BLE происходило практически в режиме реального времени, каждые 100 мс для радиомаяков BLE, передающих с частотой 10 Гц.

Вы можете прочитать полные результаты здесь:

http://developer.radiusnetworks.com/2014/10/28/android-5.0-scanning.html

Эти тесты основаны на запуске экспериментальной ветви Android-l-apis Android Beacon Library 2.0 с открытым исходным кодом.

Не очевидно, в чем разница в результатах вашего теста, но возможно, что запуск и остановка сканирования изменяют результаты.

РЕДАКТИРОВАТЬ: возможно, разница в оборудовании. Смотрите отчет о подобных таймингах на Nexus 4: https://github.com/AltBeacon/android-beacon-library/issues/59

У меня пока нет 50 репутации за комментарий, так что потерпите меня, этот комментарий будет в форме ответа. В вашем коде не должно быть этой части:

if (isScanning) { scanner.startScan(...)

будь это вместо этого:

if (!isScanning) {
 scanner.startScan(...)

Потому что, следуя вашему коду, вы вызываете stopScan() перед началом сканирования. Это может не иметь прямого влияния на результат, если метод stopScan() идемпотентен / безопасен. Но вы знаете, ради ясности кода вы должны отредактировать вопрос. И сделать то же самое с вашим кодом, иногда византийские вещи играют;)

Вы пробовали большие значения для SCAN_INTERVAL_MS? Если да, то насколько большой?

Я получил очень похожие результаты с моим Nexus 4, как в KitKat, так и в Lollipop.

С KitKat адаптер Bluetooth также со временем перестал отвечать на запросы; Сначала я подумал, что это может быть связано с коротким интервалом сканирования (200 мс), но увеличение этого числа даже до секунды не помогло, в этом отношении я обнаружил, что при отключении и включении адаптера программным путем иногда возникает проблема, К сожалению, я не могу сказать, что это работает все время.

Теперь с Lollipop, в котором я возлагал большие надежды на решение этой проблемы, я испытал то же поведение, которое вы описали. Мне также пришлось использовать реализацию startScan / stopScan, получая аналогичные результаты в отношении времени обнаружения. К сожалению, я не нашел способ обойтись быстрее.

Исходя из того, что вы описываете, я полагаю, что это может быть аппаратная проблема, хотя Nexus 7 и Nexus 4 от разных производителей (Asus и LG).

Я знаю, что не оказываю здесь никакой помощи, кроме попыток ответить на ваш вопрос о том, что вы что-то упустили; Я так не думаю, я думаю, что проблема в чем-то вроде аппаратного обеспечения или API-интерфейса Bluetooth, который все еще не ведет себя так, как должен на разных устройствах.

Помимо API 21 Android по умолчанию использует SCAN_MODE_LOW_POWER. SCAN_MODE_LOW_POWER

Попробуйте SCAN_MODE_BALANCED и посмотрите, станет ли он лучше.
SCAN_MODE_BALANCED

Если вы ищете BW13_DayOne_Session1 Bluetooth Advanced В Google вы найдете PDF-документ, в котором указаны задержки для устройств в зависимости от настроек обнаружения (см. стр. 8). Я предполагаю, что ваша проблема связана с этими временами. Вы можете проверить это, выяснив конфигурацию рекламы для устройства, которое вы тестируете (Adv Int, Duty Cycle), затем выясните, что настройки API делают для настройки интервала сканирования и т. Д. Получив их, вы можете использовать эту таблицу интерполировать, чтобы увидеть, если вы получаете результаты, которые вы ожидаете.

Я знаю, что это программный сайт, но часто при взаимодействии с аппаратным обеспечением вам нужно знать протокол, иначе вы стреляете в темноте.

Другие вопросы по тегам