Android: после нескольких сканирований подряд устройства BLE не обнаружены.Android

Форум для тех, кто программирует под Android
Ответить
Anonymous
 Android: после нескольких сканирований подряд устройства BLE не обнаружены.

Сообщение Anonymous »

Мы создали приложение для Android, которое сканирует устройства конкретных производителей с помощью BLE (Bluetooth Low Energy). На самом деле все работает нормально, но есть одна проблема. Иногда после нескольких сканирований планшет не больше находит устройства при всех последующих сканированиях. Единственный выход — выключить и снова включить Bluetooth, тогда планшет снова сможет найти устройства. В среде имеется около 30 устройств BLE, и 20 из них — это те устройства, которые мы хотели бы найти и отфильтровать. Клиент может воспроизвести его, но мы, к сожалению, не можем, поэтому его сложно отладить.
Сканирование должно работать только в переднем плане, нет необходимости сканировать в фоновом режиме. Пользователь может перезапустить сканирование сразу после его завершения. Я уже знаю ограничение в 30 секунд/5 сканирований, оно обрабатывается нормально.
Целевой планшет работает под управлением Android 8.1, настройки проекта:

Код: Выделить всё

  compileSdkVersion = 28
buildToolsVersion = '30.0.2'
minSdkVersion = 17
targetSdkVersion = 28
Настройки сканирования BLE:

Код: Выделить всё

ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
.setNumOfMatches(ScanSettings.MATCH_NUM_ONE_ADVERTISEMENT)
.setReportDelay(0L)
.build();

new ScanFilter.Builder().setManufacturerData(MANUFACTURE_ID, new byte[0]).build());
Когда сканирование работает, журналы выглядят следующим образом:

Код: Выделить всё

I/BleScanService: Start Scanning
D/Fragment: refreshUIElements true
D/BluetoothAdapter: isLeEnabled(): ON
D/BluetoothLeScanner: onScannerRegistered() - status=0 scannerId=7 mScannerId=0
I/Fragment$ScannerBroadcastReceiver: listing device BLE: deviceMacAddress:EC:A5:80:12:D4:1A, nwpBleMacAddress:XXXXXXXXXXEE, name:Device
I/Fragment$ScannerBroadcastReceiver: listing device BLE: deviceMacAddress:CE:A8:80:60:C9:A8, nwpBleMacAddress:XXXXXXXXXX08, name:Device
D/BluetoothAdapter: isLeEnabled(): ON
I/BleScanService: Stopped Scanning
Когда сканирование перестает работать, я получаю такие журналы (интересно, что запись журнала isLeEnabled(): ON больше не появляется):

Код: Выделить всё

07-28 14:02:48:310 I/BleScanService(2) : Start Scanning
07-28 14:02:48:316 I/BleScanService(2) : Resume Scanning in 0 ms
07-28 14:02:48:324 D/Fragment(2) : refreshUIElements true
07-28 14:02:54:357 I/BleScanService(2) : Stopped Scanning
Это класс ScanService, который творит всю магию:

Код: Выделить всё

public class BleScanService {

private final Handler scanHandler;
private final Context context;
private final Settings settings;
private BluetoothAdapter bluetoothAdapter;
private BluetoothLeScanner bluetoothLeScanner;

private final Set discoveredDevices;
private final List scanTimestamps = new ArrayList(10);

private boolean scanning;

@Inject
public BleScanService(Context context, Settings settings) {
this.context = context;
this.settings = settings;
this.scanHandler = new Handler();
}

public BluetoothDevice getBluetoothDevice(String deviceMacAddress) {
return bluetoothAdapter.getRemoteDevice(deviceMacAddress);
}

public boolean isScanning() {
return scanning;
}

public void startScanning() {
if (scanning) {
Timber.i("Ignore start scanning request because scanning is already active");
return;
}

try {
scanning = true;
discoveredDevices.clear();
initService();

long nextScannerAvailability = getNextScannerAvailability();
Timber.i("Resume Scanning in %s ms", nextScannerAvailability);

scanHandler.postDelayed(this::scan, nextScannerAvailability);

} catch (Exception e) {
Timber.e(e);
stopScanning();
}
}

private void scan() {
bluetoothLeScanner.startScan(BleScanSettings.getManufacturerScanFilter(), BleScanSettings.getScanSettings(), leScanCallback);
scanHandler.postDelayed(() -> {
stopScanning();
Timber.i("Devices shown %s", String.join(", ", discoveredDevices));
}, 8000);
}

private void initService() {
if (bluetoothAdapter == null) {
BluetoothManager bluetoothManager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
bluetoothAdapter = bluetoothManager.getAdapter();
}
if (bluetoothLeScanner == null) {
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
}
}
}

private long getNextScannerAvailability() {
final long currentTimeMillis = System.currentTimeMillis();
scanTimestamps.removeIf(t -> currentTimeMillis - t > 30000);

if (scanTimestamps.size() <  4) {
return 0;
}
long oldestActiveTimestamp = scanTimestamps.get(scanTimestamps.size() - 4);

return 30000 - (currentTimeMillis - oldestActiveTimestamp) + 500;
}

public void stopScanning() {
if (bluetoothAdapter == null) {
return;
}

try {
bluetoothLeScanner.stopScan(leScanCallback);
} catch (Exception e) {
Timber.w(e, "Exception occurred while attempting to stop BLE scanning.");
}
if (scanning) {
scanTimestamps.add(System.currentTimeMillis());
}

scanHandler.removeCallbacksAndMessages(null);
scanning = false;
Timber.i("Stopped Scanning");
}

protected void broadcastDetectedDevice(Intent intent) {
context.sendBroadcast(intent);
}

private final ScanCallback leScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
if (!scanning) {
Timber.i("ignore scanning result because the scanning is paused");
return;
}

if (result.getScanRecord() == null) {
Timber.i("Skip unsupported device %s", result.getDevice().getName());
return;
}

BluetoothDevice device = result.getDevice();
String deviceMacAddress = device.getAddress();

if (isAlreadyDiscovered(deviceMacAddress)) {
return;
}

byte[] manufacturerSpecificData = result.getScanRecord().getManufacturerSpecificData(BleScanSettings.MANUFACTURE_ID);
final Optional msdIntent = BleDetectedDeviceIntentHelper
.getIntentFromManufacturerSpecificData(deviceMacAddress, manufacturerSpecificData);

if (msdIntent.isPresent()) {
handleManufacturerDataScan(deviceMacAddress, msdIntent.get());
}
}

@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Timber.i("SCAN FAILED");
stopScanning();
}
Разрешения, которые используются и запрашиваются: Есть идеи? Спасибо за помощь!

Подробнее здесь: https://stackoverflow.com/questions/731 ... s-in-a-row
Ответить

Быстрый ответ

Изменение регистра текста: 
Смайлики
:) :( :oops: :roll: :wink: :muza: :clever: :sorry: :angel: :read: *x)
Ещё смайлики…
   
К этому ответу прикреплено по крайней мере одно вложение.

Если вы не хотите добавлять вложения, оставьте поля пустыми.

Максимально разрешённый размер вложения: 15 МБ.

Вернуться в «Android»