Android:调用stopScan后基于Intent的蓝牙扫描仍运行如何解决?
问题分析
调用基于Intent的BLE扫描stopScan后,过段时间仍能收到扫描结果,且原生SDK与Android-Scanner-Compat-Library均出现此问题,核心问题出在扫描任务的取消逻辑或PendingIntent的匹配机制上。
可能原因
- PendingIntent不匹配:
stopScan必须使用与startScan完全一致的PendingIntent实例(包括Intent内容、FLAG、requestCode),若stop时PendingIntent已被重建或引用丢失,系统无法匹配并取消对应的扫描任务。 - 残留扫描任务:因配置变化(如屏幕旋转)、多次调用
startScan未对应停止,导致多个扫描任务并行运行,stopScan仅取消了最新的任务,旧任务仍在后台执行。 - 上下文回收导致取消失效:调用
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
相关产品推荐
相关产品推荐

