从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
相关产品推荐
相关产品推荐

