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

Jetpack Compose中Bluetooth ACTION_STATE_CHANGED广播接收器被调用两次问题

解决Jetpack Compose中蓝牙关闭时BluetoothAdapter.ACTION_STATE_CHANGED广播触发两次的问题

在Jetpack Compose应用中监听蓝牙状态时遇到异常:关闭蓝牙时,BluetoothAdapter.ACTION_STATE_CHANGED广播接收器的onReceive方法会被触发两次,导致状态更新逻辑执行多次,不符合预期。

问题代码

class MainActivity : ComponentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var isBluetoothEnable by remember { mutableStateOf(false) }
            ScreenOne(isBluetoothEnable) { isBluetoothEnable = it }
        }
    }
}

@Composable
fun ScreenOne(isBluetoothEnable: Boolean, onBluetoothChange: (Boolean) -> Unit) {
    Text(text = "Hello world isBluetoothEnable $isBluetoothEnable")

    BluetoothStateListener {
        onBluetoothChange(it)
        Log.i("BluetoothStateListener", "isBluetoothEnable $it")
    }
}

@Composable
fun BluetoothStateListener(
    context: Context = LocalContext.current,
    isBluetoothEnable: (Boolean) -> Unit
) {
    DisposableEffect(Unit) {
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                if (intent?.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
                    val state = 
                        intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
                    isBluetoothEnable(state == BluetoothAdapter.STATE_ON)
                }
            }
        }
        val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
        context.registerReceiver(receiver, filter)
        onDispose {
            context.unregisterReceiver(receiver)
        }
    }
}

日志信息

2024-02-29 22:44:16.938 15425-15425 BluetoothStateListener  com.example.simplecomposenavigation  I  isBluetoothEnable false
2024-02-29 22:44:17.215 15425-15425 BluetoothStateListener  com.example.simplecomposenavigation  I  isBluetoothEnable true

问题根源

Android系统在蓝牙状态变化时会发送多轮广播:

  • 开启流程:先推送STATE_TURNING_ON(正在开启)状态,再推送STATE_ON(已开启)状态
  • 关闭流程:先推送STATE_TURNING_OFF(正在关闭)状态,再推送STATE_OFF(已关闭)状态

原代码仅判断状态是否等于STATE_ON,导致过渡状态(如STATE_TURNING_OFF)也会触发false回调,若系统广播推送时机有波动,就会出现多次回调的情况。另外需确认DisposableEffect的注册逻辑是否稳定,避免因Compose重组导致接收器重复注册。

修复代码

修改广播接收器的状态判断逻辑,只处理最终的STATE_ON和STATE_OFF状态,忽略过渡状态:

@Composable
fun BluetoothStateListener(
    context: Context = LocalContext.current,
    isBluetoothEnable: (Boolean) -> Unit
) {
    DisposableEffect(Unit) {
        val receiver = object : BroadcastReceiver() {
            override fun onReceive(context: Context?, intent: Intent?) {
                intent?.takeIf { it.action == BluetoothAdapter.ACTION_STATE_CHANGED }?.let {
                    val state = it.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)
                    when(state) {
                        BluetoothAdapter.STATE_ON -> isBluetoothEnable(true)
                        BluetoothAdapter.STATE_OFF -> isBluetoothEnable(false)
                        // 过滤过渡状态,避免无效回调
                    }
                }
            }
        }
        val filter = IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED)
        context.registerReceiver(receiver, filter)
        onDispose {
            context.unregisterReceiver(receiver)
        }
    }
}

额外排查点

如果修复后仍有多次回调,可检查:

  • 是否在多个可组合函数中重复调用了BluetoothStateListener
  • 是否因Activity重建(如屏幕旋转)导致接收器重复注册,可在DisposableEffect的注册/注销逻辑中添加日志,确认执行时机

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 01:07:54