gpt4 book ai didi

android - 一旦 API 失败,Coroutine 永远不会调用 API

转载 作者:行者123 更新时间:2023-11-29 18:25:55 24 4
gpt4 key购买 nike

在我的 View 模型中,我有一个使用协程调用 API 的函数。

fun loadPosts(){

GlobalScope.launch(coroutineContext){
changeState(load = true)
val list= withContext(Dispatchers.IO) {
apiService.getProfile(AUTH)
}
changeState(false,false,null)
showResult(list)
}
}

每次我点击一个按钮,这个函数被触发,API 被调用并且我得到有效的响应。但是,一旦我的 api 出现 500 或 Http 401 未经授权之类的异常,然后当我再次按下按钮时,协程就不会被调用,并且它似乎会再次从缓存中返回错误消息。

对于一个用例:

我点击按钮 -> 调用 Api -> 获得成功响应

我再次点击 -> 调用 Api -> 获得成功响应

我断开了手机与互联网的连接

我点击了按钮 -​​> 调用了 Api -> 出现类似 ConnectionError 的异常

我 Handlebars 机连上了网络

我点击了按钮 -​​> Api 没有被调用 -> 出现类似 ConnectionError 的异常

现在即使我的手机有有效的互联网连接,我按下按钮,而不是调用 api 并等待响应,它一次又一次地给我之前失败的响应。

之前我使用的是 Rxjava,但我没有遇到任何此类问题。我是协程的新手,所以如果有人有任何建议,欢迎您

最佳答案

每当您使用诸如 launch 之类的协程构建器启动协程时,您需要在给定的 CoroutineScope 中启动它 - 这是由 defined as an extension on CoroutineScope 函数强制执行的.此作用域包含一个 CoroutineContext,它将定义协程的执行方式。

根据上面的评论,我假设您使用的大致是这个设置:

abstract class BaseViewModel : ViewModel(), CoroutineScope { 
private val job: Job = Job()
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job

override fun onCleared() {
coroutineContext.cancel()
}
}

通过使用 GlobalScope.launch(coroutineContext),您实际上用上下文参数覆盖了 GlobalScope 提供的所有内容。另外,由于您的 ViewModel 本身已经是一个范围,因此您不需要首先在 GlobalScope启动。您可以在 ViewModel 中简单地写下 launch 而不指定范围(本质上是 this.launch {})并且不向它传递上下文,因为无论如何它都会从示波器中获取一个。

另一个问题是您使用常规 Job 作为 CoroutineContext 的一部分。此 Job 成为您启动的每个协程的父协程,并且每当子协程失败时,例如网络错误,父 Job 也会被取消 - 这意味着任何进一步您尝试启动的 child 也会立即失败,因为您无法在已经失败的 Job 下启动新的 child (有关详细信息,请参阅 Job documentation)。

为避免这种情况,您可以使用 SupervisorJob相反,它还可以将您的协同程序组合在一起作为子级,并在 ViewModel 被清除时取消它们,但如果其中一个子级失败则不会被取消。


所以总结一下要在代码级别快速进行的修复:

  • ViewModel 中使用 SupervisorJob(当您在那里时,只需创建一次组合的 CoroutineContext,通过直接分配它,而不是将它放在 getter 中):

    override val coroutineContext: CoroutineContext = Dispatchers.Main + SupervisorJob()
  • ViewModel 而不是 GlobalScope 中定义的范围内启动协程:

    abstract class BaseViewModel : ViewModel(), CoroutineScope { 
    // ...

    fun load() {
    launch { // equivalent to this.launch, because `this` is a CoroutineScope
    // do loading
    }
    }
    }

您可能还想考虑让您的 ViewModel 包含一个 CoroutineScope 而不是实现接口(interface)本身,as described here一个discussed here .


有很多关于这个主题的阅读,这里有一些我通常推荐的文章:

...以及 Roman Elizarov 在他博客上的所有其他内容,真的 :)

关于android - 一旦 API 失败,Coroutine 永远不会调用 API,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59064175/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com