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

BLE ScanCallback被保留无法被GC回收的原因排查

问题分析与解决思路

核心问题是系统BLE服务持有ScanCallback引用,导致SensorScanner实例无法被GC回收——调用startScan后,BluetoothLeScanner会一直保留该回调的引用;如果ScanCallback是SensorScanner的非静态内部类,还会隐式持有SensorScanner实例的引用,哪怕你把sensorScanner置为null,只要回调没被移除,整个SensorScanner就会被持续持有,无法被回收。

以下是具体解决思路:

  • 主动停止扫描并移除回调:
    不管bleAdapter有没有暴露相关方法,都可以直接通过bleAdapter.bluetoothLeScanner拿到原始的BluetoothLeScanner实例,调用stopScan传入对应的ScanCallback。建议给SensorScanner添加销毁方法:

    class SensorScanner(private val bleAdapter: BluetoothAdapter) {
        private val scanCallback = object : ScanCallback() {
            // 你的回调实现逻辑
        }
    
        fun startScan(filters: List<ScanFilter>, settings: ScanSettings) {
            bleAdapter.bluetoothLeScanner.startScan(filters, settings, scanCallback)
        }
    
        // 新增清理方法,主动释放回调引用
        fun cleanup() {
            bleAdapter.bluetoothLeScanner.stopScan(scanCallback)
        }
    }
    

    之后在置null前先调用清理方法:

    sensorScanner.cleanup()
    sensorScanner = null
    

    这样系统BLE服务会释放对ScanCallback的引用,SensorScanner实例就能被正常回收。

  • 切断回调与SensorScanner的强引用绑定:
    如果暂时无法调用stopScan,可将ScanCallback改为静态内部类(静态内部类不会持有外部类的隐式强引用),或者用WeakReference包裹SensorScanner的引用传入回调:

    class SensorScanner(private val bleAdapter: BluetoothAdapter) {
        private val scanCallback = StaticScanCallback(WeakReference(this))
    
        // 静态内部类,仅持有外部类的弱引用
        private class StaticScanCallback(private val scannerRef: WeakReference<SensorScanner>) : ScanCallback() {
            override fun onScanResult(callbackType: Int, result: ScanResult?) {
                val scanner = scannerRef.get() ?: return // 弱引用为空则终止操作
                // 处理扫描结果逻辑
            }
        }
    }
    

    这种方式至少能避免整个SensorScanner实例被泄漏。

  • 绑定扫描逻辑到组件生命周期:
    不要频繁创建和丢弃SensorScanner实例,建议把扫描逻辑与Activity/Fragment的生命周期绑定——比如在onStart时启动扫描,onStop/onDestroy时调用cleanup停止扫描并释放回调,减少无意义的实例创建与内存泄漏风险。

  • 排查BLE适配器封装问题:
    如果bleAdapter是自定义封装类,检查是否内部封装了BluetoothLeScanner但未暴露stopScan方法。这种情况要么修改封装类添加清理接口,要么直接通过系统服务获取原始BluetoothAdapter操作:

    val bluetoothManager = context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
    val originalAdapter = bluetoothManager.adapter
    originalAdapter.bluetoothLeScanner.stopScan(scanCallback)
    

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.01 11:15:37