我正在尝试从多个位置获取一些数据以填充recyclerView。我曾经使用过回调函数,效果很好,但是需要将其重构为协程。
因此,我有一份改装服务列表,并分别对其进行调用。然后,我可以使用onResponse回调更新recyclerView。我该如何用协同程序实现这一目标。
我尝试过类似的操作,但是在收到响应后会触发下一个呼叫:
runblocking {
for (service in services) {
val response = async(Dispatchers.IO) {
service.getResponseAsync()
}
adapter.updateRecyclerView(response.await())
}
}
使用另一种方法,我遇到的问题是,当我使用启动时,我无法回到主线程上来更新我的ui,无法等待响应:
runblocking {
services.foreach {
launch(Dispatcher.IO) {
val response = it.getResponseAsync()
}
withContext(Dispatcher.Main) {
adapter.updateRecyclerView(response)
}
}
}
每一个技巧我都很感谢;) 帕特里克欢呼
答案 0 :(得分:4)
从launch
而不是runBlocking
开始协程。以下示例假定您从默认使用Dispatchers.Main
的上下文中启动。如果不是这种情况,则可以使用launch(Dispatchers.Main)
。
如果您希望每次并行操作返回时都更新视图,则将UI更新移至要为每个service
项目启动的协程中:
for (service in services) {
launch {
val response = withContext(Dispatchers.IO) { service.getResponseAsync() }
adapter.updateRecyclerView(response)
}
}
如果仅在它们全部返回后才需要更新,则可以使用awaitAll
。在这里,必须编写您的updateRecyclerView
函数来处理响应列表,而不是一次。
launch {
val responses = services.map { service ->
async(Dispatchers.IO) { service.getResponseAsync() }
}
adapter.updateRecyclerView(responses.awaitAll())
}
答案 1 :(得分:1)
await()
调用将挂起当前的协程,并释放当前线程以供其他排队的协程附加。
因此,当调用await()
时,当前协程将暂停直到接收到响应为止,这就是为什么for循环未完成(在请求完成之前转到下一个迭代)的原因。
首先,您不应该在这里使用runBlocking
,强烈建议不要在生产环境中使用它。
您应该改为使用android提供的ViewModel范围进行结构化并发(如果不再需要(如活动生命周期已结束,则取消请求)。
您可以在活动或片段viewModelOwner.viewModelScope.launch(/*Other dispatcher if needed*/) {}
中使用这样的视图模型范围,或者通过附加的作业自己创建协程范围,该作业会在onDestroy上自行取消。
对于协程不执行并行请求的问题,您可以在for循环内启动多个请求而不必等待它们。
然后使用选择表达式https://kotlinlang.org/docs/reference/coroutines/select-expression.html#selecting-deferred-values
选择它们示例:
viewModelOwner.viewModelScope.launch {
val responses = mutableListOf<Deferred<TypeReturnedFromGetResponse>>()
for (service in services) {
async(Dispatchers.IO) {
service.getResponseAsync()
}.let(responses::add)
}
// adds which ever request is done first in oppose to awaiting for all then update
for (i in responses.indices) {
select<Unit> {
for (response in responses) {
response.onAwait {
adapter.updateRecyclerView(it)
}
}
}
}
}
PS:使用此方法看起来很丑陋,但是将在第一次解决请求后立即更新适配器,而不是等待每个请求然后更新其中的项目。