tri*_*ice 6 kotlin retrofit2 kotlin-coroutines
我正在尝试从多个位置获取一些数据以填充 recyclerView。我曾经使用回调,效果很好,但需要将其重构为协程。
所以我有一个改造服务列表,并称它们为并行。然后我可以使用 onResponse 回调更新 recyclerView。我怎样才能用协程来实现这一点。
我尝试过类似的事情,但在我得到回复后下一个电话被触发:
runblocking {
for (service in services) {
val response = async(Dispatchers.IO) {
service.getResponseAsync()
}
adapter.updateRecyclerView(response.await())
}
}
Run Code Online (Sandbox Code Playgroud)
使用另一种方法时,我遇到的问题是我无法返回主线程来更新我的用户界面,因为我正在使用启动并且无法等待响应:
runblocking {
services.foreach {
launch(Dispatcher.IO) {
val response = it.getResponseAsync()
}
withContext(Dispatcher.Main) {
adapter.updateRecyclerView(response)
}
}
}
Run Code Online (Sandbox Code Playgroud)
我很感激每一个提示;) 帕特里克干杯
使用launch而不是启动协程runBlocking。下面的示例假设您从Dispatchers.Main默认使用的上下文启动。如果不是这种情况,你可以使用launch(Dispatchers.Main)这些。
如果您想在每次并行操作返回时更新您的视图,请将您的 UI 更新移动到您为每个service项目启动的协程中:
for (service in services) {
launch {
val response = withContext(Dispatchers.IO) { service.getResponseAsync() }
adapter.updateRecyclerView(response)
}
}
Run Code Online (Sandbox Code Playgroud)
如果您只需要在所有这些都返回后更新,则可以使用awaitAll. 在这里,您updateRecyclerView必须编写函数来处理响应列表,而不是一次一个。
launch {
val responses = services.map { service ->
async(Dispatchers.IO) { service.getResponseAsync() }
}
adapter.updateRecyclerView(responses.awaitAll())
}
Run Code Online (Sandbox Code Playgroud)
该await()调用挂起当前协程并释放当前线程以供其他排队的协程附加。
因此,当await()调用时,当前协程将挂起直到收到响应,这就是 for 循环未完成的原因(在完成之前的请求之前进入下一次迭代)。
首先也是最重要的是,您不应该在runBlocking这里使用它,强烈建议不要在生产环境中使用它。
您应该使用 android 提供的 ViewModel 范围来实现结构化并发(如果不再需要,例如 Activity 的生命周期结束,则取消请求)。
您可以在 Activity 或 Fragment 中使用像这样的视图模型作用域,viewModelOwner.viewModelScope.launch(/*Other dispatcher if needed*/) {}或者自己创建一个协程作用域并附加一个作业,该作业会在 onDestroy 上自行取消。
对于协程不执行并行请求的问题,您可以在 for 循环内启动多个请求,而无需等待(ing)它们。
并使用选择表达式选择它们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)
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
PS:使用这种方法看起来很难看,但一旦第一次解决请求就会更新适配器,而不是等待每个请求然后更新其中的项目。
| 归档时间: |
|
| 查看次数: |
2468 次 |
| 最近记录: |