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

如何将BroadcastReceiver的蓝牙扫描数据传递到MainActivity等其他类

Android蓝牙扫描逻辑封装及设备数据回传方案

你提到的Observer模式是该场景下非常推荐的最佳实践,除此之外也可以用更轻量的接口回调方案,两种实现方式如下:


方案1:接口回调(轻量无依赖,适合简单项目)

不需要引入额外组件,实现成本最低

步骤1:定义回调接口

interface BluetoothScanCallback {
    fun onDeviceFound(device: BluetoothDevice)
    // 可选扩展其他状态回调
    fun onScanStarted() = Unit
    fun onScanFinished() = Unit
    fun onScanError(errorCode: Int) = Unit
}

步骤2:封装独立扫描管理类

将广播注册、扫描启停逻辑全部封装到独立类中,注意使用ApplicationContext避免内存泄漏

class BluetoothScanner(private val context: Context) {
    private var scanCallback: BluetoothScanCallback? = null

    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == BluetoothDevice.ACTION_FOUND) {
                val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                device?.let { scanCallback?.onDeviceFound(it) }
            }
        }
    }

    fun setScanCallback(callback: BluetoothScanCallback) {
        this.scanCallback = callback
    }

    fun startScan() {
        // 此处可自行补充蓝牙权限、蓝牙开关状态检查逻辑
        val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
        context.registerReceiver(receiver, filter)
        BluetoothAdapter.getDefaultAdapter()?.startDiscovery()
        scanCallback?.onScanStarted()
    }

    fun stopScan() {
        runCatching {
            context.unregisterReceiver(receiver)
            BluetoothAdapter.getDefaultAdapter()?.cancelDiscovery()
            scanCallback?.onScanFinished()
            scanCallback = null // 销毁回调引用避免泄漏
        }
    }
}

步骤3:MainActivity中调用

class MainActivity : AppCompatActivity() {
    private lateinit var bluetoothScanner: BluetoothScanner

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 传入ApplicationContext避免持有Activity引用
        bluetoothScanner = BluetoothScanner(applicationContext)
        bluetoothScanner.setScanCallback(object : BluetoothScanCallback {
            override fun onDeviceFound(device: BluetoothDevice) {
                val deviceName = device.name
                val macAddress = device.address
                // 扫描结果回调默认在广播线程,更新UI需要切到主线程
                runOnUiThread {
                    // 此处执行更新列表等UI操作
                }
            }

            override fun onScanStarted() {
                // 展示扫描中状态
            }

            override fun onScanFinished() {
                // 隐藏扫描中状态
            }
        })

        // 示例:点击按钮触发扫描
        btnStartScan.setOnClickListener {
            // 先申请蓝牙相关权限再调用扫描
            bluetoothScanner.startScan()
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 页面销毁时必须停止扫描,避免广播泄漏
        bluetoothScanner.stopScan()
    }
}

方案2:Observer模式(基于Jetpack LiveData,适合Jetpack生态项目)

自动感知Activity生命周期,不需要手动注销回调,原生支持UI线程回调,能有效避免内存泄漏

扫描类封装

class BluetoothScanner(private val context: Context) {
    // 扫描到的设备通过LiveData对外暴露
    val foundDeviceLiveData = MutableLiveData<BluetoothDevice>()
    val scanStateLiveData = MutableLiveData<ScanState>()

    // 扫描状态密封类
    sealed class ScanState {
        object Idle : ScanState()
        object Scanning : ScanState()
        object Finished : ScanState()
        data class Error(val code: Int) : ScanState()
    }

    private val receiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            if (intent.action == BluetoothDevice.ACTION_FOUND) {
                val device: BluetoothDevice? = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)
                device?.let { foundDeviceLiveData.postValue(it) }
            }
        }
    }

    fun startScan() {
        // 权限、蓝牙状态检查逻辑省略
        val filter = IntentFilter(BluetoothDevice.ACTION_FOUND)
        context.registerReceiver(receiver, filter)
        BluetoothAdapter.getDefaultAdapter()?.startDiscovery()
        scanStateLiveData.postValue(ScanState.Scanning)
    }

    fun stopScan() {
        runCatching {
            context.unregisterReceiver(receiver)
            BluetoothAdapter.getDefaultAdapter()?.cancelDiscovery()
            scanStateLiveData.postValue(ScanState.Finished)
        }
    }
}

MainActivity中观察数据

class MainActivity : AppCompatActivity() {
    private lateinit var bluetoothScanner: BluetoothScanner

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        bluetoothScanner = BluetoothScanner(applicationContext)

        // 观察扫描到的设备
        bluetoothScanner.foundDeviceLiveData.observe(this) { device ->
            // LiveData自动在UI线程回调,可直接更新UI
            val deviceName = device.name
            val macAddress = device.address
            // 执行列表更新等操作
        }

        // 观察扫描状态
        bluetoothScanner.scanStateLiveData.observe(this) { state ->
            when(state) {
                is BluetoothScanner.ScanState.Scanning -> // 展示加载状态
                is BluetoothScanner.ScanState.Finished -> // 隐藏加载状态
                is BluetoothScanner.ScanState.Error -> // 提示扫描错误
                else -> {}
            }
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        bluetoothScanner.stopScan()
    }
}

注意事项

  • 不同Android版本蓝牙权限要求不同:Android 12及以上需要申请BLUETOOTH_SCAN、BLUETOOTH_CONNECT权限,低版本需要申请蓝牙基础权限和定位权限
  • 如果需要过滤重复扫描到的设备,可以在扫描类中新增HashSet<String>存储已扫描到的MAC地址,重复设备不回调即可

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 20:12:03