一、ScanCallback概述
ScanCallback是Android BLE扫描的核心回调类,用于接收蓝牙低功耗(BLE)设备的扫描结果。当使用BluetoothLeScanner的startScan()方法开始扫描后,所有扫描结果都会通过这个回调返回。
二、ScanCallback的主要方法
1. onScanResult(int callbackType, ScanResult result)
触发时机:每次扫描到一个BLE设备时调用
参数说明:
-
callbackType:扫描回调类型,可能是以下值之一:-
ScanSettings.CALLBACK_TYPE_ALL_MATCHES(0):所有匹配的广告包 -
ScanSettings.CALLBACK_TYPE_FIRST_MATCH(1):第一次匹配的设备 -
ScanSettings.CALLBACK_TYPE_MATCH_LOST(2):之前匹配但现在丢失的设备
-
-
result:包含扫描到的设备信息的ScanResult对象
典型实现:
java
@Override
public void onScanResult(int callbackType, ScanResult result) {
super.onScanResult(callbackType, result);
BluetoothDevice device = result.getDevice();
String deviceName = device.getName();
String deviceAddress = device.getAddress();
int rssi = result.getRssi();
ScanRecord scanRecord = result.getScanRecord();
// 处理扫描到的设备
}
2. onBatchScanResults(List<ScanResult> results)
触发时机:当系统批量返回扫描结果时调用(不是每次扫描到设备都立即回调)
参数说明:
results:包含多个扫描结果的列表
典型实现:
java
@Override
public void onBatchScanResults(List<ScanResult> results) {
super.onBatchScanResults(results);
for (ScanResult result : results) {
// 处理每个扫描结果
}
}
3. onScanFailed(int errorCode)
触发时机:当扫描失败时调用
参数说明:
-
errorCode:错误代码,可能是以下值之一:-
ScanCallback.SCAN_FAILED_ALREADY_STARTED(1):扫描已经启动 -
ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED(2):应用注册失败 -
ScanCallback.SCAN_FAILED_INTERNAL_ERROR(3):内部错误 -
ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED(4):不支持的特性 -
ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES(5):硬件资源不足(Android 10+)
-
典型实现:
java
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
String errorMessage;
switch (errorCode) {
case ScanCallback.SCAN_FAILED_ALREADY_STARTED:
errorMessage = "Scan already started";
break;
case ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED:
errorMessage = "Application registration failed";
break;
case ScanCallback.SCAN_FAILED_INTERNAL_ERROR:
errorMessage = "Internal error";
break;
case ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED:
errorMessage = "Feature unsupported";
break;
case ScanCallback.SCAN_FAILED_OUT_OF_HARDWARE_RESOURCES:
errorMessage = "Out of hardware resources";
break;
default:
errorMessage = "Unknown error";
}
// 处理扫描失败
}
三、ScanResult详解
ScanResult对象包含扫描到的BLE设备的详细信息:
1. 获取设备基本信息
java
BluetoothDevice device = result.getDevice();
String deviceName = device.getName(); // 设备名称(可能为null)
String deviceAddress = device.getAddress(); // 设备MAC地址
int rssi = result.getRssi(); // 信号强度(dBm)
long timestampNanos = result.getTimestampNanos(); // 时间戳(纳秒)
2. 获取扫描记录(ScanRecord)
java
ScanRecord scanRecord = result.getScanRecord();
if (scanRecord != null) {
// 获取设备名称(可能比BluetoothDevice.getName()更可靠)
String scanRecordName = scanRecord.getDeviceName();
// 获取广播数据
byte[] advertiseData = scanRecord.getBytes();
// 获取服务UUID列表
List<ParcelUuid> serviceUuids = scanRecord.getServiceUuids();
// 获取制造商特定数据
byte[] manufacturerData = scanRecord.getManufacturerSpecificData(manufacturerId);
// 获取服务数据
byte[] serviceData = scanRecord.getServiceData(serviceUuid);
// 获取TX功率级别
int txPower = scanRecord.getTxPowerLevel();
}
四、扫描配置详解
1. ScanSettings - 扫描设置
java
ScanSettings settings = new 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();
扫描模式(ScanMode):
-
SCAN_MODE_LOW_POWER(0):低功耗模式,扫描间隔长 -
SCAN_MODE_BALANCED(1):平衡模式 -
SCAN_MODE_LOW_LATENCY(2):低延迟模式,扫描间隔短 -
SCAN_MODE_OPPORTUNISTIC(-1):机会主义模式(不常用)
匹配模式(MatchMode):
-
MATCH_MODE_AGGRESSIVE(1):积极匹配 -
MATCH_MODE_STICKY(2):粘性匹配
2. ScanFilter - 扫描过滤器
java
List<ScanFilter> filters = new ArrayList<>();
filters.add(new ScanFilter.Builder()
.setDeviceName("MyDevice") // 设备名称
.setDeviceAddress("00:11:22:33:44:55") // MAC地址
.setServiceUuid(ParcelUuid.fromString("0000ffe0-0000-1000-8000-00805f9b34fb")) // 服务UUID
.setServiceData(
ParcelUuid.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"),
new byte[]{0x01, 0x02}, // 服务数据
new byte[]{0xFF, 0xFF} // 服务数据掩码(可选)
)
.setManufacturerData(
0x004C, // 制造商ID(如Apple是0x004C)
new byte[]{0x01, 0x02}, // 制造商数据
new byte[]{0xFF, 0xFF} // 制造商数据掩码(可选)
)
.build());
五、完整扫描示例
java
public class BleScanner {
private BluetoothLeScanner bluetoothLeScanner;
private ScanCallback scanCallback;
public void startScan() {
BluetoothManager bluetoothManager = (BluetoothManager)
context.getSystemService(Context.BLUETOOTH_SERVICE);
BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
// 扫描设置
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
.setReportDelay(0)
.build();
// 扫描过滤器(可选)
List<ScanFilter> filters = new ArrayList<>();
filters.add(new ScanFilter.Builder()
.setServiceUuid(ParcelUuid.fromString("0000ffe0-0000-1000-8000-00805f9b34fb"))
.build());
// 扫描回调
scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
processResult(result);
}
@Override
public void onBatchScanResults(List<ScanResult> results) {
for (ScanResult result : results) {
processResult(result);
}
}
@Override
public void onScanFailed(int errorCode) {
Log.e("BleScanner", "Scan failed with error: " + errorCode);
}
private void processResult(ScanResult result) {
BluetoothDevice device = result.getDevice();
String deviceName = device.getName();
String deviceAddress = device.getAddress();
int rssi = result.getRssi();
// 处理扫描结果
}
};
// 开始扫描
bluetoothLeScanner.startScan(filters, settings, scanCallback);
}
public void stopScan() {
if (bluetoothLeScanner != null && scanCallback != null) {
bluetoothLeScanner.stopScan(scanCallback);
}
}
}