首次断开后Android蓝牙BLE扫描无法找到设备求助
安卓BLE连续Activity扫描设备失败问题分析与解决
问题描述
开发的Android项目包含两个连续执行的Activity,流程均为:扫描BLE设备→连接→执行操作→断开。第一个Activity完成流程并断开设备后,第二个Activity扫描同一设备时无法找到该设备,已确认设备处于广播状态但扫描结果无返回。
初始排查情况
- 设备绑定状态排查:通过
BluetoothAdapter.getBondedDevices()查询,无绑定设备 - 缓存清理尝试:在第二个Activity启动扫描前调用
flushPendingScanResults,问题未解决 - 外部工具验证:nRFConnect可正常扫描到目标设备,且用nRFConnect扫描后返回本APP,设备可被正常识别
核心代码
第一个Activity代码
public class BluetoothActivity { private static final String SERVICE_UUID_STRING = "12340000-58fa-4b77-8f3b-72fd2cbaa335"; private static final String CALIBRATE_CHAR_UUID_STRING = "1234efab-58fa-4b77-8f3b-72fd2cbaa335"; private BluetoothAdapter mBluetoothAdapter; private BluetoothLeScanner mBluetoothLeScanner; private BluetoothGatt mBluetoothGatt; private boolean tag10connected = false; private Handler scanTimeoutHandler = new Handler(); private Runnable scanTimeoutRunnable = new Runnable() { @SuppressLint("MissingPermission") @Override public void run() { stopScanning(); toastMessage("Scan timed out without finding target device!"); if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); mBluetoothGatt.close(); mBluetoothGatt = null; } } }; @SuppressLint("MissingPermission") public void startScanning() { Log.e("BLE", "startScanning: mBluetoothAdapter.isEnabled() = " + mBluetoothAdapter.isEnabled()); mBluetoothLeScanner.startScan(mScanCallback); } @SuppressLint("MissingPermission") private void stopScanning() { Log.e("BLE", "stopScanning: mBluetoothLeScanner = " + mBluetoothLeScanner); toastMessage("Scanning finished!"); mBluetoothLeScanner.stopScan(mScanCallback); scanTimeoutHandler.removeCallbacks(scanTimeoutRunnable); } @SuppressLint("MissingPermission") private void connectToDevice(BluetoothDevice device) { mBluetoothGatt = device.connectGatt(this, false, mGattCallback, BluetoothDevice.TRANSPORT_LE); } @SuppressLint("MissingPermission") private void disconnectDevice() { if (mBluetoothGatt != null) { BluetoothDevice device = mBluetoothGatt.getDevice(); Log.e("BLE", "Trying to disconnect: " + device.getName()); mBluetoothGatt.disconnect(); mBluetoothGatt.close(); mBluetoothGatt = null; } } private ScanCallback mScanCallback = new ScanCallback() { @SuppressLint("MissingPermission") @Override public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice device = result.getDevice(); if (device.getName() != null && device.getName().equals("TOILET")) { tag10connected = true; stopScanning(); connectToDevice(device); } } }; private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @SuppressLint("MissingPermission") @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) { if (newState == BluetoothGatt.STATE_CONNECTED) { Log.e("BLEConnection", "Connection Built!"); toastMessage("Connected!"); disconnectDevice(); } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { Log.e("BLEConnection", "Disconnected!"); toastMessage("Disconnected!"); } } @SuppressLint("MissingPermission") @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { Log.e("BLE", "onServicesDiscovered called with status: " + status); if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattCharacteristic characteristic = gatt.getService(UUID.fromString(SERVICE_UUID_STRING)) .getCharacteristic(UUID.fromString(CALIBRATE_CHAR_UUID_STRING)); characteristic.setValue(String.valueOf(0)); gatt.writeCharacteristic(characteristic); } } @SuppressLint("MissingPermission") @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { disconnectDevice(); } } }; public void toastMessage(String data) { Toast.makeText(this, data, Toast.LENGTH_LONG).show(); } }
第二个Activity代码
public class IMUCalibrationActivity { private static final String SERVICE_UUID_STRING = "45670000-58fa-4b77-8f3b-72fd2cbaa335"; private static final String CALIBRATE_CHAR_UUID_STRING = "45671111-58fa-4b77-8f3b-72fd2cbaa335"; private BluetoothAdapter mBluetoothAdapter; private BluetoothLeScanner mBluetoothLeScanner; private BluetoothGatt mBluetoothGatt; private Handler scanTimeoutHandler = new Handler(); private Runnable scanTimeoutRunnable = new Runnable() { @SuppressLint("MissingPermission") @Override public void run() { stopScanning(); toastMessage("Scan timed out without finding target device!"); if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); mBluetoothGatt.close(); mBluetoothGatt = null; } } }; @SuppressLint("MissingPermission") public void startScanning() { Log.e("BLE", "startScanning: mBluetoothAdapter.isEnabled() = " + mBluetoothAdapter.isEnabled()); toastMessage("Scanning..."); mBluetoothLeScanner.startScan(mScanCallback); scanTimeoutHandler.postDelayed(scanTimeoutRunnable, 10000); // 10 seconds timeout } @SuppressLint("MissingPermission") private void stopScanning() { Log.e("BLE", "stopScanning: mBluetoothLeScanner = " + mBluetoothLeScanner); toastMessage("Scanning finished!"); mBluetoothLeScanner.stopScan(mScanCallback); scanTimeoutHandler.removeCallbacks(scanTimeoutRunnable); } @SuppressLint("MissingPermission") private void connectToDevice(BluetoothDevice device) { mBluetoothGatt = device.connectGatt(this, false, mGattCallback, BluetoothDevice.TRANSPORT_LE); } @SuppressLint("MissingPermission") private void disconnectDevice() { if (mBluetoothGatt != null) { mBluetoothGatt.disconnect(); mBluetoothGatt.close(); mBluetoothGatt = null; Intent nextStep = new Intent(IMUCalibrationActivity.this, TimeSyncActivity.class); startActivity(nextStep); finish(); } } private ScanCallback mScanCallback = new ScanCallback() { @SuppressLint("MissingPermission") public void onScanResult(int callbackType, ScanResult result) { BluetoothDevice device = result.getDevice(); if (device.getName() != null && device.getName().equals("TOILET")) { stopScanning(); connectToDevice(device); } } }; private BluetoothGattCallback mGattCallback = new BluetoothGattCallback() { @SuppressLint("MissingPermission") @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) { runOnUiThread(() -> { if (newState == BluetoothGatt.STATE_CONNECTED) { Log.e("BLEConnection", "Connection Built!"); toastMessage("Connected!"); mBluetoothGatt.discoverServices(); } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { Log.e("BLEConnection", "Disconnected!"); toastMessage("Disconnected!"); } }); } @SuppressLint("MissingPermission") @Override public void onServicesDiscovered(BluetoothGatt gatt, int status) { Log.e("BLE", "onServicesDiscovered called with status: " + status); if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattCharacteristic characteristic = gatt.getService(UUID.fromString(SERVICE_UUID_STRING)) .getCharacteristic(UUID.fromString(CALIBRATE_CHAR_UUID_STRING)); characteristic.setValue(timestampGenerator()); gatt.writeCharacteristic(characteristic); Log.e("TimeWorker", "Writing to characteristic: " + gatt.writeCharacteristic(characteristic)); } } @SuppressLint("MissingPermission") @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { BluetoothGattCharacteristic calibrationCharacteristic = gatt.getService(UUID.fromString(SERVICE_UUID_STRING)) .getCharacteristic(UUID.fromString(CALIBRATE_CHAR_UUID_STRING)); gatt.readCharacteristic(calibrationCharacteristic); } } @SuppressLint("MissingPermission") @Override public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { Log.e("BLE", "characteristic read: " + characteristic.toString()); if (status == BluetoothGatt.GATT_SUCCESS) { final byte[] data = characteristic.getValue(); float value = ByteBuffer.wrap(data).order(ByteOrder.LITTLE_ENDIAN).getFloat(); Log.e("Got value after calibration", String.valueOf(value)); disconnectDevice(); } } }; }
问题原因分析
- Gatt资源释放不彻底:第一个Activity在
onConnectionStateChange的STATE_CONNECTED分支直接调用disconnectDevice(),此时Gatt连接流程未完全完成(未执行服务发现、特征读写),导致系统蓝牙资源未彻底释放,判定设备仍处于连接状态,从而过滤扫描结果。 - 系统BLE扫描缓存机制:安卓系统对近期连接过的BLE设备有缓存策略,短时间内重复扫描时会默认过滤该设备,避免重复返回。
- 扫描参数优先级不足:默认扫描模式为低功耗模式,扫描频率低,可能无法及时捕获设备广播包。
解决方案
方案1:修正Gatt资源释放时机
修改第一个Activity的Gatt回调,确保完整执行BLE流程后再断开设备:
// 第一个Activity的onConnectionStateChange修改 @Override public void onConnectionStateChange(BluetoothGatt gatt, int status, final int newState) { if (newState == BluetoothGatt.STATE_CONNECTED) { Log.e("BLEConnection", "Connection Built!"); toastMessage("Connected!"); // 移除直接断开,改为触发服务发现 gatt.discoverServices(); } else if (newState == BluetoothGatt.STATE_DISCONNECTED) { Log.e("BLEConnection", "Disconnected!"); toastMessage("Disconnected!"); // 此处可启动第二个Activity Intent intent = new Intent(BluetoothActivity.this, IMUCalibrationActivity.class); startActivity(intent); finish(); } }
仅在特征写入成功后执行断开,保证Gatt流程完整,资源彻底释放。
方案2:调整扫描参数,绕过系统缓存
在两个Activity的startScanning方法中,使用高优先级扫描模式并添加明确过滤规则:
@SuppressLint("MissingPermission") public void startScanning() { Log.e("BLE", "startScanning: mBluetoothAdapter.isEnabled() = " + mBluetoothAdapter.isEnabled()); // 配置低延迟扫描模式 ScanSettings settings = new ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) .build(); // 添加设备名称过滤 List<ScanFilter> filters = new ArrayList<>(); ScanFilter filter = new ScanFilter.Builder() .setDeviceName("TOILET") .build(); filters.add(filter); mBluetoothLeScanner.startScan(filters, settings, mScanCallback); }
强制系统按照指定参数扫描,避免默认缓存过滤。
方案3:延迟启动第二个Activity
在第一个Activity断开设备后,延迟2-3秒再启动第二个Activity,给系统足够时间释放蓝牙资源:
// 第一个Activity的onCharacteristicWrite修改 @Override public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) { if (status == BluetoothGatt.GATT_SUCCESS) { disconnectDevice(); // 延迟启动第二个Activity new Handler().postDelayed(() -> { Intent intent = new Intent(BluetoothActivity.this, IMUCalibrationActivity.class); startActivity(intent); finish(); }, 2000); } }
内容的提问来源于stack exchange,提问作者Grace Jin
相关产品推荐
相关产品推荐

