7

我正在尝试使用以下代码:

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 构造函数中的字段。(这甚至可能在“结构化并发”的范围内吗?)

4

2 回答 2

6

编辑:

// 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")

mutator 不是通过 的方法发生的MutableLiveData,而是通过SavedStateHandle将在外部更新 LiveData(以及因此流)的方法发生的。

于 2020-12-14T07:07:37.120 回答
1

我处于类似的位置,但我不想通过 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
}
于 2020-12-27T23:03:06.037 回答