BluetoothLeScanner — 是什么、方法以及 Android 中的 BLE 扫描

作者: IT Sectr 发布日期: 2026-07-16 阅读时间: 10 分钟

BluetoothLeScanner — 用于扫描 Bluetooth Low Energy 设备的 Android 类,从 API 21(Android 5.0)开始可用。BluetoothLeScanner 取代了 BluetoothAdapter 上已过时的 startLeScan 方法,提供了灵活的 API,包括扫描配置(ScanSettings)、过滤(ScanFilter)和后台模式支持(PendingIntent)。实例通过 BluetoothAdapter.getBluetoothLeScanner() 获取。根据 Android Developers, 2026,BluetoothLeScanner 支持三种功耗模式,并允许按服务 UUID、设备名称或 MAC 地址过滤来扫描 BLE 广播包。

要点

  • BluetoothLeScanner — 用于 BLE 扫描的现代 Android API(API 21+),替代已过时的 startLeScan
  • ScanSettings — 扫描模式配置:LOW_POWER、BALANCED、LOW_LATENCY 和回调类型
  • ScanFilter — 按服务 UUID、设备名称、MAC 地址、制造商数据过滤结果
  • ScanCallback — 结果回调 onScanResult、onBatchScanResults 和带错误代码的 onScanFailed
  • PendingIntent — 通过 BroadcastReceiver 进行后台扫描,即使用户在后台

什么是 BluetoothLeScanner:本质和获取实例

BluetoothLeScanner — 用于在 Android 上管理 BLE 扫描的系统类。与接受简单 LeScanCallback 的 BluetoothAdapter.startLeScan() 不同,BluetoothLeScanner 提供了面向对象的 API,带有设置、过滤器和扩展错误处理。该类出现在 API 21(Android 5.0)中,同时支持 BLE 4.2,并且仍然是所有现代 Android 版本上进行 BLE 扫描的主要方式。

获取 BluetoothLeScanner 实例通过 BluetoothAdapter.getBluetoothLeScanner() 完成。如果蓝牙适配器不可用(蓝牙已禁用或设备不支持 BLE),该方法返回 null。在获取之前,检查 BluetoothAdapter.isEnabled() 以及通过 PackageManager 检查 FEATURE_BLUETOOTH_LE 的存在。获取扫描器后,您可以在任何线程中启动扫描 —— Android 自行在蓝牙堆栈的内部线程上调度 BLE 操作。

kotlin
// 获取 BluetoothLeScanner
class BLEScannerManager(context: Context) {

    private val bluetoothManager: BluetoothManager =
        context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    private val adapter: BluetoothAdapter? = bluetoothManager.adapter
    private var scanner: BluetoothLeScanner? = null

    fun initScanner(): Boolean {
        // 检查 BLE 可用性
        if (!context.packageManager.hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
            return false
        }

        // 检查蓝牙是否已启用
        if (adapter?.isEnabled != true) {
            return false
        }

        // 获取扫描器
        scanner = adapter?.bluetoothLeScanner
        return scanner != null
    }

    // 检查扫描器可用性
    val isAvailable: Boolean
        get() = scanner != null

    // 启动不带过滤器的基础扫描
    fun startBasicScan() {
        scanner?.startScan(object : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult) {
                handleResult(result)
            }
        })
    }

    private fun handleResult(result: ScanResult) {
        val device = result.device
        print("Device: ${device.name ?: "Unnamed"}, RSSI: ${result.rssi}, address: ${device.address}")
    }
}

BLEScannerManager 类演示了安全获取和初始化 BluetoothLeScanner。initScanner 通过 hasSystemFeature 检查 BLE 的存在、蓝牙是否已启用以及扫描器是否成功获取。startBasicScan 启动不带设置和过滤器的扫描 —— 检测范围内的所有 BLE 设备。handleResult 解析 ScanResult:BluetoothDevice(名称、地址)、RSSI(信号强度)、scanRecord(广播数据)。

ScanSettings:扫描模式和回调类型

ScanSettings — 用于配置 BLE 扫描的类。主要参数是扫描模式(scanMode),它决定了功耗和检测延迟之间的权衡。ScanSettings.Builder 允许配置:scanMode、callbackType(CALLBACK_TYPE_ALL_MATCHES、CALLBACK_TYPE_FIRST_MATCH、CALLBACK_TYPE_MATCH_LOST)、matchMode(MATCH_MODE_AGGRESSIVE、MATCH_MODE_STICKY)、reportDelay(批量发送延迟)和 phy(PHY_LE_1M、PHY_LE_2M、PHY_LE_CODED)。

三种扫描模式:SCAN_MODE_LOW_POWER(0)—— 低功耗后台扫描,检测延迟几秒。SCAN_MODE_BALANCED(1)—— 适用于大多数场景的平衡模式。SCAN_MODE_LOW_LATENCY(2)—— 最小检测延迟(约 100 毫秒),最大功耗。对于主动搜索设备,使用 LOW_LATENCY;对于后台监控,使用 LOW_POWER。

reportDelay —— 在批量发送结果之前的毫秒延迟。如果 reportDelay = 0,则检测后立即发送结果。如果 > 0,Android 会累积结果并通过 onBatchScanResults 发送批处理。批量发送减少了回调调用的次数并降低了功耗,适用于低优先级的后台扫描。

kotlin
// 针对不同场景的 ScanSettings 配置
class ScanSettingsProvider {

    // 1. 快速扫描(主动搜索)
    fun lowLatencyScan(): ScanSettings {
        return ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .setMatchMode(ScanSettings.MATCH_MODE_AGGRESSIVE)
            .setReportDelay(0)
            .setPhy(ScanSettings.PHY_LE_ALL_SUPPORTED)
            .build()
    }

    // 2. 节能扫描(后台监控)
    fun lowPowerScan(): ScanSettings {
        return ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
            .setMatchMode(ScanSettings.MATCH_MODE_STICKY)
            .setReportDelay(2000)  // 每 2 秒一批
            .build()
    }

    // 3. BLE Long Range 扫描(Coded PHY)
    fun longRangeScan(): ScanSettings {
        return ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setPhy(ScanSettings.PHY_LE_CODED)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
            .build()
    }

    // 4. 仅在 2M PHY 上扫描(BLE 5.0+)
    fun highSpeedScan(): ScanSettings {
        return ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .setPhy(ScanSettings.PHY_LE_2M)
            .build()
    }
}

ScanSettingsProvider 类包含典型配置。lowLatencyScan —— 用于 UI 扫描("立即在此处"搜索)。lowPowerScan —— 用于后台监控,每 2 秒一批,回调类型为 FIRST_MATCH(仅在首次检测时激活)。longRangeScan 使用 PHY_LE_CODED(BLE Long Range,最远 1 公里)。highSpeedScan —— PHY_LE_2M(2 Mbit/s,仅限 BLE 5.0+ 设备)。

ScanFilter:按 UUID 和名称过滤 BLE 设备

ScanFilter —— 用于过滤 BLE 扫描结果的类。没有过滤器时,BluetoothLeScanner 返回范围内的所有 BLE 设备 —— 在密集的 BLE 环境中,这可能是每分钟数百个数据包。ScanFilter 将结果缩小到所需设备,降低功耗和应用负载。过滤器在蓝牙堆栈级别应用 —— 不匹配的数据包在传递到应用程序之前被拒绝。

过滤器类型:setServiceUuid —— 服务 UUID(必须是完整的 128 位格式)。setDeviceName —— 设备名称的子字符串(区分大小写,精确子字符串匹配)。setDeviceAddress —— 精确的 MAC 地址。setManufacturerData —— 制造商数据(公司 ID + 掩码)。一次扫描可以设置多个过滤器 —— 设备必须符合所有条件(AND 逻辑)。对于 OR 逻辑,请启动多次扫描。

kotlin
// 针对不同场景创建 ScanFilter
class ScanFilterFactory {

    // 1. 按服务 UUID 过滤(心率监测仪)
    fun byHeartRateService(): ScanFilter {
        return ScanFilter.Builder()
            .setServiceUuid(
                ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB")
            )
            .build()
    }

    // 2. 按设备名称过滤("iBeacon*")
    fun byDeviceName(): ScanFilter {
        return ScanFilter.Builder()
            .setDeviceName("Sensor")
            .build()
    }

    // 3.   MAC-(设备)
    fun byMacAddress(mac: String): ScanFilter {
        return ScanFilter.Builder()
            .setDeviceAddress(mac)
            .build()
    }

    // 4. 组合过滤器(UUID + )
    fun combinedFilter(): List<ScanFilter> {
        return listOf(
            ScanFilter.Builder()
                .setServiceUuid(
                    ParcelUuid.fromString("0000A001-0000-1000-8000-00805F9B34FB")
                )
                .setDeviceName("MyDevice")
                .build()
        )
    }

    // 5.   制造商数据
    fun byManufacturer(companyId: Int, data: ByteArray, mask: ByteArray): ScanFilter {
        return ScanFilter.Builder()
            .setManufacturerData(companyId, data, mask)
            .build()
    }
}

ScanFilterFactory 类显示所有过滤器类型。byHeartRateService 过滤具有脉搏服务 0x180D 的设备。byDeviceName 查找名称中包含 "Sensor" 的设备(Apple 建议使用唯一名称进行过滤)。byMacAddress —— 精确搜索特定设备。combinedFilter —— 按 UUID 和名称的 AND 过滤器。byManufacturer —— 按制造商数据过滤(例如,对于 iBeacon,使用 Apple 公司 ID 0x004C)。

ScanCallback:处理扫描结果和错误

ScanCallback —— 用于接收 BLE 扫描结果的抽象类。包含三个方法:onScanResult —— 单个结果(回调类型、ScanResult)、onBatchScanResults —— 针对 reportDelay > 0 的结果批处理、onScanFailed —— 错误代码。所有方法都在 Android 主线程上调用。对于 onScanResult 中的长时间处理,请使用协程或 HandlerThread。

ScanResult 包含:BluetoothDevice device(设备)、int rssi(信号强度,单位 dBm)、ScanRecord scanRecord(广播数据)、long timestampNanos(自系统启动以来的检测时间)。ScanRecord 提供:getServiceData() —— UUID + 自定义数据、getManufacturerSpecificData() —— 制造商数据、getAdvertiseFlags() —— BLE 标志。回调类型(callbackType)指示:CALLBACK_TYPE_ALL_MATCHES —— 与过滤器匹配、CALLBACK_TYPE_FIRST_MATCH —— 首次检测、CALLBACK_TYPE_MATCH_LOST —— 设备丢失。

onScanFailed 的错误代码:SCAN_FAILED_ALREADY_STARTED(1)—— 扫描已启动、SCAN_FAILED_APPLICATION_REGISTRATION_FAILED(2)—— 应用程序在蓝牙堆栈中的注册失败、SCAN_FAILED_INTERNAL_ERROR(3)—— 堆栈内部错误、SCAN_FAILED_FEATURE_UNSUPPORTED(4)—— 设备不支持 BLE 扫描。

kotlin
// 完整扫描结果和错误处理
class ScanResultHandler {

    private val results = mutableListOf<ScanResult>()

    val scanCallback = object : ScanCallback() {

        // 1. 单个结果
        override fun onScanResult(callbackType: Int, result: ScanResult) {
            // callbackType:1 = ALL_MATCHES,2 = FIRST_MATCH,4 = MATCH_LOST
            if (callbackType == ScanSettings.CALLBACK_TYPE_MATCH_LOST) {
                onDeviceLost(result)
                return
            }

            // 添加到列表(按地址去重)
            val existingIndex = results.indexOfFirst {
                it.device.address == result.device.address
            }
            if (existingIndex >= 0) {
                results[existingIndex] = result  // 更新 RSSI
            } else {
                results.add(result)
            }

            // 从广播包中提取数据
            val record = result.scanRecord
            val serviceData = record?.serviceData
            val manufacturerData = record?.manufacturerSpecificData

            print("Found: ${result.device.name ?: "N/A"}, RSSI: ${result.rssi}")
        }

        // 2. 批处理结果(reportDelay > 0)
        override fun onBatchScanResults(results: MutableList<ScanResult>?) {
            results?.let { batch ->
                print("Batch: ${batch.size} devices")
            }
        }

        // 3. 扫描错误
        override fun onScanFailed(errorCode: Int) {
            val error = when (errorCode) {
                ScanCallback.SCAN_FAILED_ALREADY_STARTED -> "Already scanning"
                ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED -> "Registration failed"
                ScanCallback.SCAN_FAILED_INTERNAL_ERROR -> "Internal error"
                ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED -> "BLE not supported"
                else -> "Unknown error: $errorCode"
            }
            print("Error: $error")
        }
    }

    private fun onDeviceLost(result: ScanResult) {
        results.removeAll { it.device.address == result.device.address }
        print("Device lost: ${result.device.address}")
    }
}

ScanResultHandler 类处理蓝牙扫描仪的所有回调类型。onScanResult 更新设备列表,按 MAC 地址去重 —— 已找到的设备的 RSSI 会更新。CALLBACK_TYPE_MATCH_LOST 表示设备丢失(从列表中移除)。onBatchScanResults 处理 reportDelay > 0 的批处理结果。onScanFailed 将错误代码映射为可读消息 —— 对于 BLE 扫描调试至关重要。

PendingIntent:通过 BroadcastReceiver 进行后台 BLE 扫描

PendingIntent 扫描 —— 蓝牙扫描仪的后台 BLE 扫描机制,即使在应用程序在后台时也能工作(受 Android 8+ 限制)。代替 ScanCallback,使用 PendingIntent,在检测到 BLE 设备时向系统 BroadcastReceiver 发送广播。这允许应用程序接收有关 BLE 设备的通知,而无需在内存中(系统在接收广播时创建进程)。

后台扫描限制:在 Android 8+(API 26)上,后台服务受到限制 —— PendingIntent 扫描通过 BroadcastReceiver 绕过了这一限制,系统可以在收到 BLE 事件时启动该接收器。在 Android 10+(API 29)上,后台 BLE 扫描进一步受到制造商节能策略的限制(小米、华为、三星阻止后台 BLE 操作)。对于关键的 BLE 场景,需要带有前台服务的通知。

kotlin
// 通过 PendingIntent 进行后台 BLE 扫描
class BackgroundBLEScanner(private val context: Context) {

    private val scanner: BluetoothLeScanner? by lazy {
        val adapter = BluetoothAdapter.getDefaultAdapter()
        adapter?.bluetoothLeScanner
    }

    fun startBackgroundScan() {
        // 为 BroadcastReceiver 创建 PendingIntent
        val intent = Intent(context, BLEBroadcastReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(
            context,
            0,
            intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )

        // 后台扫描设置
        val settings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
            .setCallbackType(ScanSettings.CALLBACK_TYPE_FIRST_MATCH)
            .setMatchMode(ScanSettings.MATCH_MODE_STICKY)
            .build()

        // 启动后台扫描
        scanner?.startScan(
            null,  // 过滤器
            settings,
            pendingIntent
        )
    }

    fun stopBackgroundScan() {
        val intent = Intent(context, BLEBroadcastReceiver::class.java)
        val pendingIntent = PendingIntent.getBroadcast(
            context, 0, intent,
            PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
        )
        scanner?.stopScan(pendingIntent)
    }
}

// BroadcastReceiver   BLE-
class BLEBroadcastReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        // 获取扫描结果
        val results = BluetoothLeScanner.getPendingIntentScanResults(intent)
        results?.let { scanResults ->
            for (result in scanResults) {
                // 向用户发送通知
                showNotification(context, result.device.name ?: " ")
            }
        }
    }

    private fun showNotification(context: Context, name: String) {
        val notification = Notification.Builder(context, "ble_channel")
            .setSmallIcon(android.R.drawable.ic_dialog_info)
            .setContentTitle("BLE devices")
            .setContentText("Found: $name")
            .setAutoCancel(true)
            .build()
        val manager = context.getSystemService(Context.NOTIFICATION_SERVICE)
                as NotificationManager
        manager.notify(System.currentTimeMillis().toInt(), notification)
    }
}

BackgroundBLEScanner 类通过 PendingIntent 启动后台 BLE 扫描。startBackgroundScan 创建一个 PendingIntent,当检测到 BLE 设备时,它会向 BLEBroadcastReceiver 发送广播。BroadcastReceiver 通过 getPendingIntentScanResults() 提取 ScanResult,并可以显示通知或将数据发送到服务器。即使应用程序已被系统终止,这种方法也能工作 —— Android 在接收广播时启动 BroadcastReceiver。

在 Kotlin 中使用 BluetoothLeScanner 的 BLE 扫描器示例

完整示例 在 Kotlin 中的 BLE 扫描器,使用 BluetoothLeScanner 和 ScanSettings、ScanFilter、ScanCallback 来查找心率监测仪设备。扫描器显示找到的设备列表,包含 RSSI 和服务 UUID,并可通过 BluetoothGatt 进行连接。

kotlin
// 在 Kotlin 中使用协程的完整 BLE 扫描器
class DeviceScanner(private val context: Context) {

    private val adapter: BluetoothAdapter? by lazy {
        val manager = context.getSystemService(Context.BLUETOOTH_SERVICE)
                as BluetoothManager
        manager.adapter
    }

    private val scanner: BluetoothLeScanner? by lazy {
        adapter?.bluetoothLeScanner
    }

    fun startScan(duration: Long = 10000): Flow<ScanResult> = callbackFlow {
        // 检查蓝牙状态
        if (adapter?.isEnabled != true) {
            close(IllegalStateException("Bluetooth disabled"))
            return@callbackFlow
        }

        // 扫描配置
        val settings = ScanSettings.Builder()
            .setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
            .build()

        val filters = listOf(
            ScanFilter.Builder()
                .setServiceUuid(ParcelUuid.fromString("0000180D-0000-1000-8000-00805F9B34FB"))
                .build()
        )

        val callback = object : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult) {
                trySend(result)
            }

            override fun onScanFailed(errorCode: Int) {
                close(BLEException("Scan failed: $errorCode"))
            }
        }

        // 开始扫描
        scanner?.startScan(filters, settings, callback)

        // 持续时间后自动停止
        delay(duration)
        scanner?.stopScan(callback)
        close()
    }.flowOn(Dispatchers.IO)

    fun stop() {
        scanner?.stopScan(object : ScanCallback() {})
    }
}

class BLEException(message: String) : Exception(message)

DeviceScanner 类使用 Kotlin Flow(callbackFlow)进行响应式 BLE 扫描。扫描使用 LOW_LATENCY 设置和 Heart Rate Service UUID 过滤器启动。结果通过 onScanResult 发送到 Flow。在指定持续时间后自动停止(默认为 10 秒)。FlowOn(Dispatchers.IO) 将 BLE 操作移动到后台线程。这种方法允许在 MVVM 架构中通过 viewModelScope.launch 和 collect 使用 BLE 扫描。

常见问题

Android 中的 BluetoothLeScanner 是什么?

BluetoothLeScanner —— 用于 BLE 扫描的 Android 类(API 21+)。通过 BluetoothAdapter.getBluetoothLeScanner() 获取。支持三种扫描模式(LOW_POWER、BALANCED、LOW_LATENCY),按 UUID、名称和 MAC 地址过滤,批处理结果以及用于后台扫描的 PendingIntent。替代已过时的 BluetoothAdapter.startLeScan() 方法。

LOW_POWER 和 LOW_LATENCY 有什么区别?

SCAN_MODE_LOW_POWER —— 后台模式,检测延迟 5–10 秒,功耗最低。SCAN_MODE_LOW_LATENCY —— 主动模式,延迟约 100 毫秒,功耗最高。SCAN_MODE_BALANCED —— 折衷方案(约 2 秒延迟)。对于 UI 扫描,使用 LOW_LATENCY;对于后台监控,使用 LOW_POWER 和 PendingIntent。

为什么 BluetoothLeScanner 找不到设备?

原因:蓝牙已禁用(检查 adapter.isEnabled)、未授予权限(API 31+ 上的 BLUETOOTH_SCAN、API 23–30 上的 ACCESS_FINE_LOCATION)、scanner = null(适配器不可用)、设备超出范围或使用了错误的过滤器。同时检查 onScanFailed —— 错误代码将指示原因:SCAN_FAILED_ALREADY_STARTED(1)或 SCAN_FAILED_APPLICATION_REGISTRATION_FAILED(2)。

如何在 Android 上后台扫描 BLE?

使用 PendingIntent 版本的 startScan() —— 传递 PendingIntent 而不是 ScanCallback。当检测到 BLE 设备时,Android 向 BroadcastReceiver 发送广播,即使应用程序在后台,系统也可以启动该接收器。对于 Android 8+,将 BroadcastReceiver 添加到清单中。在 Android 10+ 上,考虑制造商的节能限制。

一次扫描可以检测到多少个 BLE 设备?

BluetoothLeScanner 对可检测设备数量没有限制 —— 限制取决于环境的 BLE 饱和度。办公室中可能有 20–50 个活动 BLE 设备,购物中心可能有数百个。对于过滤,使用 ScanFilter(按 UUID、名称)。不过滤时,异步处理结果 —— onScanResult 可能每秒被调用数十次。

总结

  • BluetoothLeScanner —— 用于 BLE 扫描的现代 Android 类(API 21+),带有设置和过滤
  • ScanSettings —— 三种模式:LOW_POWER(后台)、BALANCED(平衡)、LOW_LATENCY(主动)
  • ScanFilter —— 按服务 UUID、设备名称、MAC 地址、制造商数据过滤,使用 AND 逻辑
  • ScanCallback —— onScanResult(单个)、onBatchScanResults(批处理)、onScanFailed(错误代码)
  • PendingIntent —— 通过 BroadcastReceiver 进行后台扫描,应用终止后仍可工作
  • ScanRecord —— BLE 广播数据:serviceData、manufacturerSpecificData、advertiseFlags、TX power level
  • Kotlin Flow —— callbackFlow 允许在响应式架构中使用 BluetoothLeScanner 并自动停止

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读