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

