4. 传统BLE API基本调用:扫描回调、GATT连接、服务发现
本章带你深入Android传统BLE API(即 BluetoothLeScanner 系列)的基本调用链路。你将掌握从扫描外设到GATT连接、服务发现、特征操作的完整实现,并理解该API在Android 5.0+ 设备上的行为特性。后续章节的向后兼容方案都将以此为基础展开。
4.1 核心调用流程
传统BLE API的核心调用可拆解为以下关键步骤,每一步都依赖特定的回调或方法:
- 初始化
BluetoothLeScanner实例; - 实现
ScanCallback并调用startScan(); - 在
onScanResult()中解析外设并确认目标设备; - 通过
BluetoothDevice.connectGatt()发起GATT连接; - 重写
BluetoothGattCallback.onConnectionStateChange()监听连接结果; - 连接成功后调用
BluetoothGatt.discoverServices(); - 在
onServicesDiscovered()中获取服务和特征值; - 对特征值进行读写或订阅操作。
4.2 调用流程图
4.3 关键API与回调对比
| 角色 | 类 / 接口 | 核心方法/回调 | 用途 |
|---|---|---|---|
| 扫描启动器 | BluetoothLeScanner |
startScan() |
发起BLE扫描 |
| 扫描结果回调 | ScanCallback |
onScanResult() |
每发现一个外设调用一次 |
| GATT连接器 | BluetoothDevice |
connectGatt() |
建立GATT连接 |
| GATT状态回调 | BluetoothGattCallback |
onConnectionStateChange() |
连接/断连通知 |
| 服务发现完成 | BluetoothGattCallback |
onServicesDiscovered() |
服务列表就绪 |
| 特征值操作 | BluetoothGatt |
readCharacteristic()/setCharacteristicNotification() |
读写或订阅通知 |
4.4 代码示例:扫描 → 连接 → 发现服务 → 特征读取
关键前提:确保已声明
BLUETOOTH_SCAN、BLUETOOTH_CONNECT 权限,并在运行时请求 ACCESS_FINE_LOCATION(Android 11 以下)或 BLUETOOTH_CONNECT(Android 12+)。// 1. 初始化 BluetoothLeScanner
BluetoothManager manager = (BluetoothManager) getSystemService(BLUETOOTH_SERVICE);
BluetoothAdapter adapter = manager.getAdapter();
BluetoothLeScanner scanner = adapter.getBluetoothLeScanner();
if (scanner == null) {
// 设备不支持 BLE 或蓝牙未开启
return;
}
// 2. 实现扫描回调
private ScanCallback scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
BluetoothDevice device = result.getDevice();
int rssi = result.getRssi();
if (device.getName() != null && device.getName().startsWith("MyBleDevice")) {
scanner.stopScan(this);
// 3. 连接 GATT
device.connectGatt(context, false, gattCallback);
}
}
@Override
public void onScanFailed(int errorCode) {
// 处理扫描失败
}
};
// 4. 开始扫描
scanner.startScan(scanCallback);
// 5. GATT 回调
private BluetoothGattCallback gattCallback = new BluetoothGattCallback() {
@Override
public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {
if (newState == BluetoothProfile.STATE_CONNECTED) {
gatt.discoverServices();
} else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
// 处理断连
}
}
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
BluetoothGattService service = gatt.getService(
UUID.fromString("0000180f-0000-1000-8000-00805f9b34fb"));
if (service != null) {
BluetoothGattCharacteristic characteristic = service.getCharacteristic(
UUID.fromString("00002a19-0000-1000-8000-00805f9b34fb"));
gatt.readCharacteristic(characteristic);
}
}
}
@Override
public void onCharacteristicRead(BluetoothGatt gatt,
BluetoothGattCharacteristic characteristic,
int status) {
if (status == BluetoothGatt.GATT_SUCCESS) {
byte[] value = characteristic.getValue();
// 解析数据
}
}
};
多个外设同时连接时,需要在
onConnectionStateChange 中根据 BluetoothDevice 对象区分设备。推荐设计一个设备连接状态机来处理并发。4.5 常见错误与调试
- 扫描失败:
SCAN_FAILED_APPLICATION_REGISTRATION_FAILED(权限未授予)或SCAN_FAILED_FEATURE_UNSUPPORTED(芯片不支持后台扫描)。 - 连接失败:
onConnectionStateChange中status != GATT_SUCCESS,可能原因包括设备已连接、GATT 数据库损坏或超出连接数量限制。 - 服务发现为空:
getService()返回 null,检查 UUID 是否匹配,或在onServicesDiscovered中轮询getServices()列表。 - 无回调触发:未在正确线程调用
connectGatt()或未持有BluetoothGatt强引用,系统可能回收回调对象。
扫描到连接操作之间不要调用
BluetoothLeScanner.stopScan() 以外的同步操作,避免触发系统级扫描锁竞争导致后续 startScan 返回 SCAN_FAILED_ALREADY_STARTED。4.6 性能与稳定性优化
- 扫描功耗控制:使用
startScan(List<ScanFilter>, ScanSettings, ScanCallback)设置合理的扫描间隔和扫描模式(SCAN_MODE_LOW_LATENCY或SCAN_MODE_BALANCED)。 - 连接复用:对同一外设避免重复调用
connectGatt(),先检查BluetoothDevice.getGatt()或维护连接状态缓存。 - 服务发现超时:在
onConnectionStateChange成功后的500ms延迟内调用discoverServices(),防止系统 I/O 阻塞导致发现失败。 - 线程隔离:所有 GATT 操作方法
readCharacteristic、writeCharacteristic必须在BluetoothGattCallback的回调线程中顺序执行。