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

Android View应用中优化ViewModel接收DataStore数据后的蓝牙连接逻辑

问题分析

原方案的核心问题在于:

  1. 两个StateFlow单独收集,每次单个设置更新都会触发连接检查,当两个设置先后就绪时,会重复调用socket.connect()
  2. 缺少连接状态校验,多次并发调用connect()会导致蓝牙连接冲突,引发莫名断开
  3. Socket资源未与生命周期绑定,存在泄漏风险

优化方案

1. 合并流避免重复触发

使用combine操作符合并两个设置流,仅当两个设置均有效时才触发连接逻辑,从根源上减少重复调用。

2. 添加连接状态锁

通过isConnecting和isConnected变量严格控制连接操作的执行时机,防止并发冲突。

3. 规范IO线程调度

将蓝牙连接的阻塞操作放在Dispatchers.IO执行,避免阻塞主线程。

4. 生命周期绑定资源管理

页面销毁时主动关闭Socket,连接失败时清理无效资源,防止泄漏。


优化后代码示例

SettingsViewModel(可保持不变,或提前合并流)

@HiltViewModel
class SettingsViewModel @Inject constructor(
    private val myRepository: MyRepository
) : ViewModel() {

    val setting1State: StateFlow<String> = myRepository.getSetting1().stateIn(
        viewModelScope,
        SharingStarted.WhileSubscribed(5000),
        ""
    )
    val setting2State: StateFlow<String> = myRepository.getSetting2().stateIn(
        viewModelScope,
        SharingStarted.WhileSubscribed(5000),
        ""
    )

    // 可选:在ViewModel提前合并流,简化Activity逻辑
    val combinedSettings: StateFlow<Pair<String, String>> = combine(setting1State, setting2State) { s1, s2 ->
        Pair(s1, s2)
    }.stateIn(
        viewModelScope,
        SharingStarted.WhileSubscribed(5000),
        Pair("", "")
    )
}

MainActivity(核心优化)

@AndroidEntryPoint
class MainActivity : AppCompatActivity() {

    private val settingsViewModel: SettingsViewModel by viewModels()
    private var socket: BluetoothSocket? = null
    private var isConnected = false
    private var isConnecting = false
    private var currentSettings: Pair<String, String>? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launch {
            repeatOnLifecycle(Lifecycle.State.STARTED) {
                // 合并两个设置流,仅当两者均非空时处理
                settingsViewModel.combinedSettings.collect { (s1, s2) ->
                    if (s1.isNotEmpty() && s2.isNotEmpty()) {
                        val newSettings = Pair(s1, s2)
                        // 仅当设置变化或未连接时尝试连接
                        if (currentSettings != newSettings || !isConnected) {
                            currentSettings = newSettings
                            // 若已连接,先断开再重建
                            if (isConnected) {
                                lifecycleScope.launch(Dispatchers.IO) {
                                    socket?.close()
                                    socket = null
                                    isConnected = false
                                }
                            }
                            attemptBluetoothConnection()
                        }
                    }
                }
            }
        }
    }

    private suspend fun attemptBluetoothConnection() {
        // 防止重复连接:正在连接或已连接时直接返回
        if (isConnecting || isConnected) return
        
        isConnecting = true
        try {
            withContext(Dispatchers.IO) {
                // 延迟初始化Socket,确保线程安全
                if (socket == null) {
                    socket = createBluetoothSocket() // 替换为你的Socket创建逻辑
                }
                socket?.connect()
            }
            isConnected = true
            // 连接成功后的逻辑(如更新UI、发送数据)
        } catch (e: IOException) {
            e.printStackTrace()
            withContext(Dispatchers.IO) {
                // 连接失败时清理无效Socket
                socket?.close()
                socket = null
            }
            isConnected = false
        } finally {
            isConnecting = false
        }
    }

    override fun onDestroy() {
        super.onDestroy()
        // 页面销毁时释放蓝牙资源
        lifecycleScope.launch(Dispatchers.IO) {
            socket?.close()
            socket = null
            isConnected = false
        }
    }

    // 替换为你原有的Socket创建逻辑
    private fun createBluetoothSocket(): BluetoothSocket? {
        // 示例:从BluetoothDevice获取RFCOMM Socket
        // return bluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString("你的服务UUID"))
        return null
    }
}

核心优化点
  • 流合并:combine操作符确保只有两个设置均就绪时才触发连接,避免单个更新导致的重复调用
  • 状态锁:isConnecting和isConnected彻底杜绝并发连接请求,解决重复connect()引发的冲突
  • IO线程隔离:蓝牙连接的阻塞操作放在Dispatchers.IO,避免ANR
  • 资源清理:页面销毁时主动关闭Socket,连接失败时重置资源,防止泄漏
  • 设置变更处理:支持设置更新时自动重建连接,适配业务需求

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 16:13:15