Android 12+设备BLE扫描ScanResult.scanRecord.bytes数据异常求助
问题分析与解决方案
核心问题根源
- 硬编码解析的不可靠性:你当前通过固定字符串位置截取UUID、Major、Minor和电池信息的方式,依赖Android 10扫描记录的固定结构,但Android 12+可能调整了扫描记录中AD(Advertising Data)单元的顺序、新增系统相关AD数据,导致固定位置完全失效。
- 权限配置的潜在限制:
BLUETOOTH_SCAN权限添加的neverForLocation标记,会告知系统你的App不依赖蓝牙扫描获取位置信息,部分场景下系统会截断非必要的扫描数据,包括Beacon的核心识别字段。
具体解决方案
1. 调整权限配置
如果你的App需要识别Beacon(哪怕不直接用于定位),移除BLUETOOTH_SCAN的neverForLocation标记,同时确保动态申请权限(Android 12+要求运行时请求BLUETOOTH_SCAN):
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
运行时权限申请代码示例:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { requestPermissions(arrayOf(Manifest.permission.BLUETOOTH_SCAN), REQUEST_BLUETOOTH_SCAN) }
2. 配置标准扫描参数
显式设置ScanSettings,避免系统默认优化导致数据丢失:
val scanSettings = ScanSettings.Builder() .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY) // 低延迟模式适配实时扫描 .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES) .build() // 启动扫描时传入配置 bluetoothLeScanner.startScan(null, scanSettings, scanCallback)
3. 按BLE标准AD结构解析扫描记录
BLE扫描记录由多个AD单元组成,每个单元格式为「长度 + 类型 + 数据」,必须按此结构解析才能兼容所有Android版本:
override fun onScanResult(callbackType: Int, result: ScanResult?) { result?.scanRecord?.bytes?.let { scanRecordBytes -> var index = 0 while (index < scanRecordBytes.size) { val length = scanRecordBytes[index].toInt() and 0xFF if (length == 0) break index++ val adType = scanRecordBytes[index].toInt() and 0xFF index++ val adData = scanRecordBytes.copyOfRange(index, index + length - 1) index += length - 1 // 解析iBeacon厂商特定数据(AD类型0xFF,苹果厂商ID 0x004C) if (adType == 0xFF && adData.size >= 25) { if (adData[0] == 0x4C.toByte() && adData[1] == 0x00.toByte()) { // 解析UUID val uuidBytes = adData.copyOfRange(2, 18) val uuid = String.format( "%02X%02X%02X%02X-%02X%02X-%02X%02X-%02X%02X-%02X%02X%02X%02X%02X%02X", uuidBytes[0], uuidBytes[1], uuidBytes[2], uuidBytes[3], uuidBytes[4], uuidBytes[5], uuidBytes[6], uuidBytes[7], uuidBytes[8], uuidBytes[9], uuidBytes[10], uuidBytes[11], uuidBytes[12], uuidBytes[13], uuidBytes[14], uuidBytes[15] ) // 解析Major(大端序) val major = ((adData[18].toInt() and 0xFF) shl 8) or (adData[19].toInt() and 0xFF) // 解析Minor(大端序) val minor = ((adData[20].toInt() and 0xFF) shl 8) or (adData[21].toInt() and 0xFF) } } // 解析自定义电池信息(需匹配你的Beacon实际AD类型,示例为0x16) if (adType == 0x16 && adData.size >= 1) { val batteryLevel = adData[0].toInt() and 0xFF // 转换为0-100%(根据你的Beacon定义调整) } } } }
4. 验证扫描数据差异
在Android 12+设备上打印完整扫描记录的十六进制字符串,和Android 10的结果对比,确认AD单元的结构变化(如新增单元、顺序调整),确保解析逻辑覆盖所有可能的AD类型。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

