如何在主线程上使用Kotlin协同程序await()

时间:2017-03-02 16:48:15

标签: android kotlin kotlin-coroutines

我刚刚开始学习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())
        }
    }
}

我不明白我怎样才能在launch上下文中使用main方法。

不幸的是,我无法找到有关the official tutorial for coroutines上某些特定主题的结果的信息。

5 个答案:

答案 0 :(得分:9)

修改

另见an official example in Kotlin repo

您需要实现Continuation界面,该界面可以回调Android UI线程和Coroutine context

e.g。 (来自here

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)
}

然后尝试:

launch(Android) {
    val aVal = a.await()
    val bVal = b.await()
    resultTV.setText((aVal * bVal).toString()) 
}

更多信息:

https://medium.com/@macastiblancot/android-coroutines-getting-rid-of-runonuithread-and-callbacks-cleaner-thread-handling-and-more-234c0a9bd8eb#.r2buf5e6h

答案 1 :(得分:9)

您应使用< NEED UI thread here >项目UI模块中的kotlinx-coroutines-android上下文替换代码中的#to_sql。它的用法在kotlinx.coroutines中有很多例子进行了解释。

答案 2 :(得分:4)

首先包括为Android设计的正确库

<强> 的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"
    }
  }
}

然后您可以自由使用 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)
    }

}

对于那些在gradle中使用 kotlin experimental 作为 .aar .apk公开项目的人 到其他项目模块 - 请记住,当您使用kotlin实验性父模块/项目时,必须接受 kotlin实验

kotlin {
    experimental {
        coroutines "enable"
    }
  }

答案 3 :(得分:0)

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;

}

答案 4 :(得分:0)

此问题可能在OP提出问题后2.5年,但在类似情况下仍可能会帮助其他人。

与上面接受的答案相比,无需使用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
}