Flow未监听Singleton变量变更导致Composable未重组问题
核心原因
你当前的代码逻辑存在关键漏洞:MutableStateFlow仅在初始化阶段读取了CustomSingleton.customVar的值,但后续CustomSingleton.customVar被修改时,没有触发currentScreenId发射新的状态值。
StateFlow的订阅者(即你的Composable)只会在Flow的value被主动更新时收到通知,而普通Singleton变量的变更属于无感知的字段修改,不会自动关联到Flow的状态更新逻辑,因此Composable不会触发重组。
解决方案
针对该问题,推荐按优先级选择以下处理方式:
1. 让Singleton变量本身具备可观察性(最优解)
直接将CustomSingleton里的customVar改为基于StateFlow的实现,让它成为可观察的数据源:
object CustomSingleton { private val _customVarFlow = MutableStateFlow(0) // 替换为你的初始值 val customVarFlow: StateFlow<Int> = _customVarFlow var customVar: Int get() = _customVarFlow.value set(value) { _customVarFlow.value = value } }
随后在ViewModel中直接订阅该Flow并转换为可共享的StateFlow:
val currentScreenIdState: StateFlow<Int> = CustomSingleton.customVarFlow .stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), CustomSingleton.customVar )
这样只要CustomSingleton.customVar被修改,customVarFlow就会自动发射新值,ViewModel的currentScreenIdState同步更新后,就能触发Composable重组。
2. 给Singleton添加监听回调(无法修改Singleton内部时使用)
如果不能改动CustomSingleton的内部实现,可以为其添加监听机制,让ViewModel感知变量变化:
object CustomSingleton { private var _customVar = 0 // 原变量 var customVar: Int get() = _customVar set(value) { if (_customVar != value) { _customVar = value // 变量变化时通知所有监听者 listeners.forEach { it.invoke(value) } } } private val listeners = mutableListOf<(Int) -> Unit>() // 添加监听方法 fun addVarChangeListener(listener: (Int) -> Unit) { listeners.add(listener) } // 移除监听方法 fun removeVarChangeListener(listener: (Int) -> Unit) { listeners.remove(listener) } }
接着在ViewModel中注册监听,同步更新Flow的值:
val currentScreenId = MutableStateFlow(CustomSingleton.customVar) val currentScreenIdState: StateFlow<Int> = currentScreenId.stateIn( viewModelScope, SharingStarted.WhileSubscribed(5_000), CustomSingleton.customVar ) init { // 创建监听回调,更新Flow值 val changeListener = { newValue: Int -> currentScreenId.value = newValue } CustomSingleton.addVarChangeListener(changeListener) // ViewModel销毁时移除监听,避免内存泄漏 viewModelScope.coroutineContext.job.invokeOnCompletion { CustomSingleton.removeVarChangeListener(changeListener) } }
3. 直接在修改Singleton变量的地方同步更新ViewModel(不推荐)
如果上述两种方式都无法实现,只能在所有修改CustomSingleton.customVar的代码处,手动更新ViewModel的currentScreenId.value。但这种方式耦合性极高,代码维护成本大,不建议使用。
内容的提问来源于stack exchange,提问作者NullPointerException

