尝试将 SavedStateHandle.getLiveData() 公开为 MutableStateFlow,但 UI 线程冻结
Trying to expose SavedStateHandle.getLiveData() as MutableStateFlow, but the UI thread freezes
我正在尝试使用以下代码:
suspend fun <T> SavedStateHandle.getStateFlow(
key: String,
initialValue: T? = get(key)
): MutableStateFlow<T?> = this.let { handle ->
withContext(Dispatchers.Main.immediate) {
val liveData = handle.getLiveData<T?>(key, initialValue).also { liveData ->
if (liveData.value === initialValue) {
liveData.value = initialValue
}
}
val mutableStateFlow = MutableStateFlow(liveData.value)
val observer: Observer<T?> = Observer { value ->
if (value != mutableStateFlow.value) {
mutableStateFlow.value = value
}
}
liveData.observeForever(observer)
mutableStateFlow.also { flow ->
flow.onCompletion {
withContext(Dispatchers.Main.immediate) {
liveData.removeObserver(observer)
}
}.onEach { value ->
withContext(Dispatchers.Main.immediate) {
if (liveData.value != value) {
liveData.value = value
}
}
}.collect()
}
}
}
我正在尝试这样使用它:
// in a Jetpack ViewModel
var currentUserId: MutableStateFlow<String?>
private set
init {
runBlocking(viewModelScope.coroutineContext) {
currentUserId = state.getStateFlow("currentUserId", sessionManager.chatUserFlow.value?.uid)
// <--- this line is never reached
}
}
UI 线程冻结。我有一种感觉,这是因为 collect()
,因为我正在尝试创建一个由封闭的协程上下文管理的内部订阅,但我还需要将这个 StateFlow 作为一个字段。还有值的交叉写入(如果其中一个发生变化,如果它是新值则更新另一个)。
总的来说,collect()
的问题似乎正在暂停,因为我从未真正到达 getStateFlow()
之后的行。
有谁知道创建流的“内部订阅”而又不会最终冻结周围线程的好方法?需要 runBlocking {
以便我可以将值同步分配给 ViewModel 构造函数中的字段。 (这在 'structured concurrency' 的范围内甚至可能吗?)
编辑:
// For more details, check: https://gist.github.com/marcellogalhardo/2a1ec56b7d00ba9af1ec9fd3583d53dc
fun <T> SavedStateHandle.getStateFlow(
scope: CoroutineScope,
key: String,
initialValue: T
): MutableStateFlow<T> {
val liveData = getLiveData(key, initialValue)
val stateFlow = MutableStateFlow(initialValue)
val observer = Observer<T> { value ->
if (value != stateFlow.value) {
stateFlow.value = value
}
}
liveData.observeForever(observer)
stateFlow.onCompletion {
withContext(Dispatchers.Main.immediate) {
liveData.removeObserver(observer)
}
}.onEach { value ->
withContext(Dispatchers.Main.immediate) {
if (liveData.value != value) {
liveData.value = value
}
}
}.launchIn(scope)
return stateFlow
}
原版:
您可以借助 SavedStateHandle 中的内置通知系统,这样
val state = savedStateHandle.getLiveData<State>(Key).asFlow().shareIn(viewModelScope, SharingStarted.Lazily)
...
savedStateHandle.set(Key, "someState")
变异器不是通过 MutableLiveData
的方法发生的,而是通过 SavedStateHandle
发生的,它将在外部更新 LiveData(以及因此流)。
我处于类似的位置,但我不想通过 LiveData 修改值(如在已接受的解决方案中)。我只想使用流,而将 LiveData 作为状态句柄的实现细节。
我也不想有一个var
并在init块中初始化它。我更改了您的代码以满足这两个约束,并且它不会阻塞 UI 线程。这将是语法:
val currentUserId: MutableStateFlow<String?> = state.getStateFlow("currentUserId", viewModelScope, sessionManager.chatUserFlow.value?.uid)
我提供了一个作用域,并用它来启动处理流的 onCompletion 和集合的协程。这是完整的代码:
fun <T> SavedStateHandle.getStateFlow(
key: String,
scope: CoroutineScope,
initialValue: T? = get(key)
): MutableStateFlow<T?> = this.let { handle ->
val liveData = handle.getLiveData<T?>(key, initialValue).also { liveData ->
if (liveData.value === initialValue) {
liveData.value = initialValue
}
}
val mutableStateFlow = MutableStateFlow(liveData.value)
val observer: Observer<T?> = Observer { value ->
if (value != mutableStateFlow.value) {
mutableStateFlow.value = value
}
}
liveData.observeForever(observer)
scope.launch {
mutableStateFlow.also { flow ->
flow.onCompletion {
withContext(Dispatchers.Main.immediate) {
liveData.removeObserver(observer)
}
}.collect { value ->
withContext(Dispatchers.Main.immediate) {
if (liveData.value != value) {
liveData.value = value
}
}
}
}
}
mutableStateFlow
}
我正在尝试使用以下代码:
suspend fun <T> SavedStateHandle.getStateFlow(
key: String,
initialValue: T? = get(key)
): MutableStateFlow<T?> = this.let { handle ->
withContext(Dispatchers.Main.immediate) {
val liveData = handle.getLiveData<T?>(key, initialValue).also { liveData ->
if (liveData.value === initialValue) {
liveData.value = initialValue
}
}
val mutableStateFlow = MutableStateFlow(liveData.value)
val observer: Observer<T?> = Observer { value ->
if (value != mutableStateFlow.value) {
mutableStateFlow.value = value
}
}
liveData.observeForever(observer)
mutableStateFlow.also { flow ->
flow.onCompletion {
withContext(Dispatchers.Main.immediate) {
liveData.removeObserver(observer)
}
}.onEach { value ->
withContext(Dispatchers.Main.immediate) {
if (liveData.value != value) {
liveData.value = value
}
}
}.collect()
}
}
}
我正在尝试这样使用它:
// in a Jetpack ViewModel
var currentUserId: MutableStateFlow<String?>
private set
init {
runBlocking(viewModelScope.coroutineContext) {
currentUserId = state.getStateFlow("currentUserId", sessionManager.chatUserFlow.value?.uid)
// <--- this line is never reached
}
}
UI 线程冻结。我有一种感觉,这是因为 collect()
,因为我正在尝试创建一个由封闭的协程上下文管理的内部订阅,但我还需要将这个 StateFlow 作为一个字段。还有值的交叉写入(如果其中一个发生变化,如果它是新值则更新另一个)。
总的来说,collect()
的问题似乎正在暂停,因为我从未真正到达 getStateFlow()
之后的行。
有谁知道创建流的“内部订阅”而又不会最终冻结周围线程的好方法?需要 runBlocking {
以便我可以将值同步分配给 ViewModel 构造函数中的字段。 (这在 'structured concurrency' 的范围内甚至可能吗?)
编辑:
// For more details, check: https://gist.github.com/marcellogalhardo/2a1ec56b7d00ba9af1ec9fd3583d53dc
fun <T> SavedStateHandle.getStateFlow(
scope: CoroutineScope,
key: String,
initialValue: T
): MutableStateFlow<T> {
val liveData = getLiveData(key, initialValue)
val stateFlow = MutableStateFlow(initialValue)
val observer = Observer<T> { value ->
if (value != stateFlow.value) {
stateFlow.value = value
}
}
liveData.observeForever(observer)
stateFlow.onCompletion {
withContext(Dispatchers.Main.immediate) {
liveData.removeObserver(observer)
}
}.onEach { value ->
withContext(Dispatchers.Main.immediate) {
if (liveData.value != value) {
liveData.value = value
}
}
}.launchIn(scope)
return stateFlow
}
原版:
您可以借助 SavedStateHandle 中的内置通知系统,这样
val state = savedStateHandle.getLiveData<State>(Key).asFlow().shareIn(viewModelScope, SharingStarted.Lazily)
...
savedStateHandle.set(Key, "someState")
变异器不是通过 MutableLiveData
的方法发生的,而是通过 SavedStateHandle
发生的,它将在外部更新 LiveData(以及因此流)。
我处于类似的位置,但我不想通过 LiveData 修改值(如在已接受的解决方案中)。我只想使用流,而将 LiveData 作为状态句柄的实现细节。
我也不想有一个var
并在init块中初始化它。我更改了您的代码以满足这两个约束,并且它不会阻塞 UI 线程。这将是语法:
val currentUserId: MutableStateFlow<String?> = state.getStateFlow("currentUserId", viewModelScope, sessionManager.chatUserFlow.value?.uid)
我提供了一个作用域,并用它来启动处理流的 onCompletion 和集合的协程。这是完整的代码:
fun <T> SavedStateHandle.getStateFlow(
key: String,
scope: CoroutineScope,
initialValue: T? = get(key)
): MutableStateFlow<T?> = this.let { handle ->
val liveData = handle.getLiveData<T?>(key, initialValue).also { liveData ->
if (liveData.value === initialValue) {
liveData.value = initialValue
}
}
val mutableStateFlow = MutableStateFlow(liveData.value)
val observer: Observer<T?> = Observer { value ->
if (value != mutableStateFlow.value) {
mutableStateFlow.value = value
}
}
liveData.observeForever(observer)
scope.launch {
mutableStateFlow.also { flow ->
flow.onCompletion {
withContext(Dispatchers.Main.immediate) {
liveData.removeObserver(observer)
}
}.collect { value ->
withContext(Dispatchers.Main.immediate) {
if (liveData.value != value) {
liveData.value = value
}
}
}
}
}
mutableStateFlow
}