You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

首次断开后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();
            }
        }
    };
}

问题原因分析

  1. Gatt资源释放不彻底:第一个Activity在onConnectionStateChange的STATE_CONNECTED分支直接调用disconnectDevice(),此时Gatt连接流程未完全完成(未执行服务发现、特征读写),导致系统蓝牙资源未彻底释放,判定设备仍处于连接状态,从而过滤扫描结果。
  2. 系统BLE扫描缓存机制:安卓系统对近期连接过的BLE设备有缓存策略,短时间内重复扫描时会默认过滤该设备,避免重复返回。
  3. 扫描参数优先级不足:默认扫描模式为低功耗模式,扫描频率低,可能无法及时捕获设备广播包。

解决方案

方案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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.20 07:34:51