web-dev-qa-db-ja.com

メインスレッドでKotlinコルーチンawait()を使用する方法

Kotlinコルーチンの学習を始めたばかりで、UIに結果を表示して、長時間のAPI呼び出しをシミュレートしようとしていました。

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コンテキストで使用する方法がわかりません。

残念ながら、 コルーチンの公式チュートリアル で特定のスレッドの結果を提供することについては何も見つかりませんでした。

10

編集

Kotlinリポジトリの公式例 も参照してください

Android UIスレッドと コルーチンコンテキスト にコールバックを行う 継続 インターフェースを実装する必要があります

例えば( ここ から)

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

10
pt2121

コード内の< NEED UI thread here >kotlinx.coroutines プロジェクトのkotlinx-coroutines-AndroidモジュールからのUIコンテキストに置き換える必要があります。その使用法は コルーチンを使用したUIプログラミングのガイド でかなりの数の例で説明されています。

9
Roman Elizarov

まず第一に、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)
    }

}

gradlekotlin Experimentalを使用してプロジェクトを公開する場合は。aarまたは。apk他のプロジェクトモジュールへ-覚えておいてください。 kotlin実験的親モジュール/プロジェクトを再使用する場合は、kotlin実験的も受け入れる必要があります

kotlin {
    experimental {
        coroutines "enable"
    }
  }
4
murt

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;

}
0
nAndroid

この回答は、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
}
0
Oke Uwechue