安卓系统-连续扫描几次后,无法找到BLE设备



我们创建了一个安卓应用程序,通过使用BLE(蓝牙低能耗(扫描特定制造商的设备。事实上一切都很好,但我们有一个问题。有时,在几次扫描后,平板电脑可以no在随后的所有扫描中再找到任何设备。唯一的解决方法是关闭蓝牙并再次打开,然后平板电脑可以再次找到设备。该环境有大约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<String> discoveredDevices;
private final List<Long> 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<Intent> 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();
}

使用和请求的权限:

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.BLUETOOTH"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.CAMERA" />

有什么想法吗?谢谢你的帮助!

您描述的行为发生在应用程序扫描过于频繁时,即每30秒扫描5次以上。由于某种原因,你似乎扫描得太频繁了。。。

延迟8秒后自动停止并重新启动扫描可以解决问题。

此外,从Android 12+开始,应用程序需要BLUETOOTH_SCAN权限才能扫描蓝牙设备。

将此行添加到您的应用程序清单:

<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />

以下是请求许可的示例代码:

public void requestBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (this.checkSelfPermission(Manifest.permission.BLUETOOTH_SCAN) != PackageManager.PERMISSION_GRANTED) {
if (ActivityCompat.shouldShowRequestPermissionRationale(getActivity(),
Manifest.permission.BLUETOOTH_SCAN)) {
// display permission rationale in snackbar or dialog message 
} else {
this.requestPermissions(new String[]{
Manifest.permission.BLUETOOTH_SCAN
}, MyActivity.REQUEST_BLUETOOTH_PERMISSIONS);
}
}
}
}

最新更新