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

从Proto Datastore取Base URL初始化Ktor Client的阻塞实现问题

从Proto Datastore获取Base URL初始化Ktor Client的阻塞方案问题与解决

问题背景

我正尝试从Proto Datastore中获取Base URL,用于初始化Ktor Client实例。我知道如何从Datastore获取数据,但不知道如何阻塞执行直到拿到该值,以便用Base URL初始化Client。

我的Ktor Client服务依赖NetworkURLS类,该类有返回Base URL的方法。

获取TerminalDetails的Flow实现

以下是我从Proto Datastore获取terminalDetails的属性:

val getTerminalDetails: Flow<TerminalDetails> = cxt.terminalDetails.data
        .catch { e ->
            if (e is IOException) {
                Log.d("Error", e.message.toString())
                emit(TerminalDetails.getDefaultInstance())
            } else {
                throw e
            }
        }

常规非阻塞获取方式

通常我会这样获取值:

private fun getTerminalDetailsFromStore() {
        try {
            viewModelScope.launch(Dispatchers.IO) {
                localRepository.getTerminalDetails.collect {
                    _terminalDetails.value = it
                }
            }
        } catch(e: Exception) {
            Log.d("AdminSettingsViewModel Error", e.message.toString()) // TODO: Handle Error Properly
        }
    }

遇到的核心问题

但现在我需要从函数中返回terminalDetails.backendHost,问题随之而来:我知道需要用协程作用域获取值以避免挂起函数,但如何阻止函数在协程完成前返回?

我尝试过async和runBlocking,但async不符合预期,runBlocking会导致整个应用挂起:

fun backendURL(): String = runBlocking {
        var url: String = "localhost"

        val job = CoroutineScope(Dispatchers.IO).async {
            repo.getTerminalDetails.collect {
                it.backendHost
            }
        }

        url
    }

临时解决方案(不推荐长期使用)

编辑:以下是我的临时解决方案,我不打算长期使用。runBlocking{}的问题在于Flow<T>不会结束,导致runBlocking{}持续阻塞应用。

fun backendURL(): String {
        val details = MutableStateFlow<TerminalDetails>(TerminalDetails.getDefaultInstance())

        val job = CoroutineScope(Dispatchers.IO).launch {
            repo.getTerminalDetails.collect {
                details.value = it
            }
        }

        runBlocking {
            delay(250L)
        }

        return details.value.backendHost
    }

最终可行解决方案

编辑2:我彻底解决了问题。我创建了一个与属性同名的方法(个人选择),使用runBlocking{}和Flow<T>.first()在获取值时阻塞。我没有替换原属性的原因是,有些场景下我可以正常使用协程,无需初始化组件。

val getTerminalDetails: Flow<TerminalDetails> = cxt.terminalDetails.data
        .catch { e ->
            if (e is IOException) {
                Log.d("Error", e.message.toString())
                emit(TerminalDetails.getDefaultInstance())
            } else {
                throw e
            }
        }

    fun getTerminalDetails(): TerminalDetails = runBlocking {
        cxt.terminalDetails.data.first()
    }

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 07:54:17