[英]How to use Kotlin coroutines await() on main thread
I just started learning Kotlin coroutines and was trying to simulate some long time API-calls with showing the result on the UI:我刚开始学习 Kotlin 协程,并试图模拟一些长时间的 API 调用并在 UI 上显示结果:
class MainActivity : AppCompatActivity() {
fun log(msg: String) = println("[${Thread.currentThread().name}] $msg")
override
fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.setContentView(R.layout.activity_main)
val resultTV = findViewById(R.id.text) as TextView
val a = async(CommonPool) {
delay(1_000L)
6
}
val b = async(CommonPool) {
delay(1_000L)
7
}
launch(< NEED UI thread here >) {
val aVal = a.await()
val bVal = b.await()
resultTV.setText((aVal * bVal).toString())
}
}
}
I don't understand how could I possibly use launch
method with main
context.我不明白我怎么可能在
main
上下文中使用launch
方法。
Unfortunately, I was not able to find anything about delivering results for some specific threads on the official tutorial for coroutines .不幸的是,我无法在协程的官方教程中找到有关为某些特定线程交付结果的任何信息。
Edit : 编辑 :
Also see an official example in Kotlin repo 另请参阅Kotlin回购中的官方示例
you need to implement Continuation interface which makes a callback onto Android UI thread and Coroutine context 你需要实现Continuation接口,它可以回调到Android UI线程和Coroutine上下文
private class AndroidContinuation<T>(val cont: Continuation<T>) : Continuation<T> by cont {
override fun resume(value: T) {
if (Looper.myLooper() == Looper.getMainLooper()) cont.resume(value)
else Handler(Looper.getMainLooper()).post { cont.resume(value) }
}
override fun resumeWithException(exception: Throwable) {
if (Looper.myLooper() == Looper.getMainLooper()) cont.resumeWithException(exception)
else Handler(Looper.getMainLooper()).post { cont.resumeWithException(exception) }
}
}
object Android : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
AndroidContinuation(continuation)
}
Then try: 然后尝试:
launch(Android) {
val aVal = a.await()
val bVal = b.await()
resultTV.setText((aVal * bVal).toString())
}
more info: 更多信息:
https://medium.com/@macastiblancot/android-coroutines-getting-rid-of-runonuithread-and-callbacks-cleaner-thread-handling-and-more-234c0a9bd8eb#.r2buf5e6h https://medium.com/@macastiblancot/android-coroutines-getting-rid-of-runonuithread-and-callbacks-cleaner-thread-handling-and-more-234c0a9bd8eb#.r2buf5e6h
You shall replace < NEED UI thread here >
in your code with UI
context from kotlinx-coroutines-android
module of kotlinx.coroutines project. 您应该使用来自kotlinx.coroutines项目的
kotlinx-coroutines-android
模块的UI
上下文替换代码中的< NEED UI thread here >
。 Its usage is explained in the Guide to UI programming with coroutines with quite a few examples. 它的用法在使用协同程序的UI编程指南中进行了解释,其中有很多示例。
First of all include right library designed for Android 首先包括为Android设计的正确库
build.gradle 的build.gradle
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android{
...
dependencies{
...
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.19.3"
}
kotlin {
experimental {
coroutines "enable"
}
}
}
Then you are free to use UI 然后你可以自由使用UI
suspend private fun getFilteredGList(enumList: List<EnumXXX>) = mList.filter {
...
}
private fun filter() {
val enumList = listOf(EnumX1, EnumX2)
launch(UI){
val filteredList = getFilteredList(enumList)
setMarkersOnMap(filteredList)
}
}
For those that expose project using kotlin experimental in gradle
as .aar or .apk to other projects module - Just remember, when You're using kotlin experimental parent modules/project have to accept kotlin experimental as well 对于那些使用kotlin实验在
gradle
项目暴露为.aar或.apk到其他项目模块的人 - 请记住,当你使用kotlin时,实验父模块/项目必须接受kotlin实验以及
kotlin {
experimental {
coroutines "enable"
}
}
There are a couple of useful tools that can be used for the purpose of long time running API-calls in Activity
/ Fragment
.有几个有用的工具可用于在
Activity
/ Fragment
中长时间运行 API 调用。 So basically if you want to run two long running tasks in parallel and update UI after both are finished you can do it the next way:所以基本上如果你想并行运行两个长时间运行的任务并在完成后更新 UI,你可以通过下一种方式进行:
lifecycleScope.launch {
// launching two tasks in parallel
val aValDeferred = executeLongRunningTask1Async()
val bValDeferred = executeLongRunningTask2Async()
// wait for both of them are finished
val aVal = aValDeferred.await()
val bVal = bValDeferred.await()
// update UI
resultTV.setText((aVal * bVal).toString())
}
private fun executeLongRunningTask1Async(): Deferred<Int> = lifecycleScope.async(Dispatchers.Default) {
delay(1_000L)
6
}
private fun executeLongRunningTask2Async(): Deferred<Int> = lifecycleScope.async(Dispatchers.Default) {
delay(1_000L)
7
}
lifecycleScope
- is a CoroutineScope
, by default it has Dispatchers.Main
context, it means we can update UI in the launch
block. lifecycleScope
- 是一个CoroutineScope
,默认情况下它有Dispatchers.Main
上下文,这意味着我们可以在launch
块中更新 UI。 For LifecycleScope
, use androidx.lifecycle:lifecycle-runtime-ktx:2.4.0
or higher.对于
LifecycleScope
,使用androidx.lifecycle:lifecycle-runtime-ktx:2.4.0
或更高版本。
lifecycleScope.async(Dispatchers.Default)
- here Dispatchers.Default
is used as a context of the coroutine to have the async
block running in the background thread. lifecycleScope.async(Dispatchers.Default)
- 此处Dispatchers.Default
用作协程的上下文以使async
块在后台线程中运行。
Anko has a wrapper to do it very simply-- see: https://github.com/Kotlin/anko/wiki/Anko-Coroutines Anko有一个非常简单的包装器 - 请参阅: https : //github.com/Kotlin/anko/wiki/Anko-Coroutines
private fun doCallAsync() = async(UI) {
val user = bg { getUser() }
val name = user.await().name
val nameView = findViewById(R.id.name) as TextView
nameView.text = name;
}
This answer may be 2.5yrs after the OP's question but it may still help out others in a similar situation. 在OP的问题之后,这个答案可能是2。5年,但在类似的情况下它可能仍然有助于其他人。
The original goal can be achieved in a far simpler way than the accepted answer above without the use of async/await (statements 1, 2 and 3 will be executed in sequence, with their associated delays behaving as expected): 在不使用async / await的情况下,可以以比上面接受的答案更简单的方式实现原始目标(语句1,2和3将按顺序执行,其相关延迟表现得如预期的那样):
override fun onCreate(savedInstanceState: Bundle?) {
:
:
:
:
GlobalScope.launch(Dispatchers.Main) {
val aVal = a() // statement 1
val bVal = b() // statement 2
resultTV.setText((aVal * bVal).toString()) // statement 3
}
:
:
}
suspend fun a(): Int {
delay(1_000L)
return 6
}
suspend fun b(): Int {
delay(1_000L)
return 7
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.