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

Android:调用stopScan后基于Intent的蓝牙扫描仍运行如何解决?

问题分析

调用基于Intent的BLE扫描stopScan后,过段时间仍能收到扫描结果,且原生SDK与Android-Scanner-Compat-Library均出现此问题,核心问题出在扫描任务的取消逻辑或PendingIntent的匹配机制上。

可能原因
  1. PendingIntent不匹配:stopScan必须使用与startScan完全一致的PendingIntent实例(包括Intent内容、FLAG、requestCode),若stop时PendingIntent已被重建或引用丢失,系统无法匹配并取消对应的扫描任务。
  2. 残留扫描任务:因配置变化(如屏幕旋转)、多次调用startScan未对应停止,导致多个扫描任务并行运行,stopScan仅取消了最新的任务,旧任务仍在后台执行。
  3. 上下文回收导致取消失效:调用stopScan时使用的Context已被销毁(如Activity已finish),导致系统无法处理取消请求。
解决方案与代码修改建议

1. 确保PendingIntent复用与正确销毁

  • 复用startScan时创建的PendingIntent实例,禁止在stopScan时重新创建;
  • 调用stopScan后,主动取消PendingIntent并置空,避免残留引用:
private fun stopBluetoothLeScanIntent() {
    pendingIntent?.let {
        BluetoothLeScannerCompat.getScanner().stopScan(requireContext(), it, 0)
        it.cancel()
        pendingIntent = null
    }
}

2. 用ViewModel保存PendingIntent,避免配置变化丢失引用

配置变化(如屏幕旋转)会导致Activity重建,若PendingIntent仅存在于Activity中,重建后会丢失原引用,无法取消旧扫描任务。将PendingIntent存入ViewModel:

// 定义ViewModel
class BleScanViewModel : ViewModel() {
    var pendingIntent: PendingIntent? = null
}

// 在Activity/Fragment中使用
private val viewModel: BleScanViewModel by viewModels()

private fun startBluetoothLeScanIntent() {
    val filters = ArrayList<ScanFilter>()
    val filter = ScanFilter.Builder()
         .setServiceUuid(ParcelUuid(UUID.fromString(UUID_STRING)))
         .build()
    filters.add(filter)

    val settings = ScanSettings.Builder()
        .setScanMode(ScanSettings.SCAN_MODE_LOW_POWER)
        .build()

    val intent = Intent(requireContext(), BluetoothReceiver::class.java)
    intent.putExtra("test", "")
    
    viewModel.pendingIntent = PendingIntent.getBroadcast(requireContext(), 0, intent, FLAG_UPDATE_CURRENT or FLAG_MUTABLE)

    BluetoothLeScannerCompat.getScanner().startScan(
        filters,
        settings,
        requireContext(),
        viewModel.pendingIntent,
        0
    )
}

private fun stopBluetoothLeScanIntent() {
    viewModel.pendingIntent?.let {
        BluetoothLeScannerCompat.getScanner().stopScan(requireContext(), it, 0)
        it.cancel()
        viewModel.pendingIntent = null
    }
}

3. 在组件生命周期中确保停止扫描

在Activity/Fragment的onDestroy或onStop中强制调用stopScan,避免因组件销毁导致扫描任务残留:

override fun onDestroy() {
    super.onDestroy()
    stopBluetoothLeScanIntent()
}

4. 增加日志排查

在startScan、stopScan和BroadcastReceiver的onReceive中添加日志,确认扫描任务的启动/取消状态及接收的扫描来源:

private fun startBluetoothLeScanIntent() {
    // ... 其他代码 ...
    Log.d(TAG, "BLE扫描已启动,PendingIntent哈希值: ${viewModel.pendingIntent?.hashCode()}")
}

private fun stopBluetoothLeScanIntent() {
    viewModel.pendingIntent?.let {
        Log.d(TAG, "BLE扫描已停止,PendingIntent哈希值: ${it.hashCode()}")
        // ... 取消逻辑 ...
    }
}

class BluetoothReceiver : BroadcastReceiver() {
    companion object {
        private val TAG = BluetoothReceiver::class.simpleName
    }

    override fun onReceive(context: Context, intent: Intent) {
        Log.d(TAG, "收到BLE扫描结果,Intent内容: ${intent.toString()}")
        val hasBleExtra = intent.hasExtra(BluetoothLeScannerCompat.EXTRA_CALLBACK_TYPE)
        if (hasBleExtra) {
            val bleCallbackType = intent.getIntExtra(BluetoothLeScannerCompat.EXTRA_CALLBACK_TYPE, -1)
            if (bleCallbackType != -1) {
                // ... 处理逻辑 ...
            }
        }
    }
}

内容的提问来源于stack exchange,提问作者yjw

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 09:17:08