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

Android 12+设备BLE扫描ScanResult.scanRecord.bytes数据异常求助

问题分析与解决方案

核心问题根源

  1. 硬编码解析的不可靠性:你当前通过固定字符串位置截取UUID、Major、Minor和电池信息的方式,依赖Android 10扫描记录的固定结构,但Android 12+可能调整了扫描记录中AD(Advertising Data)单元的顺序、新增系统相关AD数据,导致固定位置完全失效。
  2. 权限配置的潜在限制: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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 08:35:16