Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Introduce Kotlin Coroutine(@CA.kt_2017/06)

AAkira
June 15, 2017

Introduce Kotlin Coroutine(@CA.kt_2017/06)

CA.kt @CyberAgent
Introduce Kotlin Coroutine
https://cyberagent.connpass.com/event/57963/

AAkira

June 15, 2017
Tweet

More Decks by AAkira

Other Decks in Technology

Transcript

  1. Kotlin Coroutine入門
    AAkira @ CA.kt

    View Slide

  2. $ whois
    private lateinit var aakira: User
    data class User(val name: String, val twitterId: String,
    val githubId: String, val company: String)
    println("Name : ${aakira.name}")
    println("Twitter Id : ${aakira.twitterId}")
    println("Github Id : ${aakira.githubId}")
    println("Company : ${aakira.company}")

    View Slide

  3. @_a_akira
    AAkira
    CyberAgent, Inc.
    Akira Aratani
    private lateinit var aakira : User
    data class User(val name: String, val twitterId: String, val githubId: String, val company: String)
    print("Name : ${aakira.name}”)
    println("Github Id : ${aakira.githubId}")
    print("Twitter Id : ${aakira.twitterId}")
    println("Company : ${aakira.company}")
    $ whois

    View Slide

  4. @_a_akira
    AAkira
    CyberAgent, Inc.
    Akira Aratani
    private lateinit var aakira : User
    data class User(val name: String, val twitterId: String, val githubId: String, val company: String)
    print("Name : ${aakira.name}”)
    println("Github Id : ${aakira.githubId}")
    print("Twitter Id : ${aakira.twitterId}")
    println("Company : ${aakira.company}")
    $ whois
    ,PUMJOॿ૸ಡຊ΋ॻ͍ͨIUUQTHPPHM"$K')

    View Slide

  5. About
    • 生放送配信プラットフォーム ≠ AbemaTV
    • 最近 アプリを縦化!
    FRESH! AndroidアプリのUI/UX : https://developers.cyberagent.co.jp/blog/archives/7177
    • Full Kotlin
    • 使ってるLibraryは大体最新

    (Kotlin1.1, Rx2, Dagger2, Retrofit2, Ok3, ExoPlayer2 etc)
    • もちろんコルーチンも一部で導入済み

    View Slide

  6. 私とKotlin
    M1 2012-04-12
    M11 2015-03-19
    M14 2015-10-01
    1.0-beta4 2015-12-22
    M13 2015-09-16
    1.0 2016-02-16
    1.0-RC 2016-02-04
    2016-01-21 Release
    2015-04 開発開始
    kotlin
    FRESH
    1.1 2017-03-01
    2017-05-15 Renewal

    View Slide

  7. About coroutine

    View Slide

  8. About Coroutine
    • Kotlin 1.1で導入された非同期プログラミングのための機能
     ≠ Thread : 特定のスレッドに属していないので、再開先は別のスレッドで動作可能
    • 1.1ではまだExperimentalなので、仕様変更の可能性あり
    • 言語機能ではなく、ライブラリとして提供されている

    View Slide

  9. Introduce Coroutine
    def kotlinCoroutinesVersion = "0.15"
    // for core
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion”
    // for JDK8
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$kotlinCoroutinesVersion”
    // for Asynchronous IO on JDK7+
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-nio:$kotlinCoroutinesVersion"
    // for RxJava2
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlinCoroutinesVersion"
    // for Android
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
    // for Swing
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-swing:$kotlinCoroutinesVersion"
    // for JavaFX
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-javafx:$kotlinCoroutinesVersion"

    View Slide

  10. Introduce Coroutine
    // for core
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion”
    // for JDK8
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$kotlinCoroutinesVersion”
    // for Asynchronous IO on JDK7+
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-nio:$kotlinCoroutinesVersion"
    // for RxJava2
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlinCoroutinesVersion"
    // for Android
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
    // for Swing
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-swing:$kotlinCoroutinesVersion"
    // for JavaFX
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-javafx:$kotlinCoroutinesVersion"
    Ұൠతͳ"OESPJEͰ։ൃͰඞཁͳϥΠϒϥϦ

    View Slide

  11. Elements of coroutine
    • Suspending functions
    • Coroutine builders (Suspending lambda)
    • Coroutine dispatchers (Coroutine context)

    View Slide

  12. Suspending functions

    View Slide

  13. Suspending functions
    • コルーチンの実行を中断することができる関数
    • 通常のコードから呼び出すことはできない
    • 他のSuspending function, Coroutine Builder(Suspending lambda)からのみ
    呼び出すことが可能

    View Slide

  14. Suspending functions
    suspend fun hoge(): Int {
    delay(10, TimeUnit.SECONDS)
    return 100
    }
    10秒後に100を返すsuspend関数

    View Slide

  15. Suspending functions
    delayもsuspend関数
    suspend fun delay(time: Long, unit: TimeUnit = TimeUnit.MILLISECONDS) {
    require(time >= 0) { "Delay time $time cannot be negative" }
    if (time <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation ->
    val delay = cont.context[ContinuationInterceptor] as? Delay
    if (delay != null)
    delay.scheduleResumeAfterDelay(time, unit, cont)
    else
    cont.cancelFutureOnCompletion(
    scheduledExecutor.schedule(ResumeRunnable(cont), time, unit))
    }
    }

    View Slide

  16. Suspending functions
    suspend fun hoge(): Int {
    delay(10, TimeUnit.SECONDS)
    return 100
    }
    hogeもsuspend関数なのでdelayを呼び出せる

    View Slide

  17. Suspending functions
    suspend関数を引数にとることも可能
    foo({
    delay(10, TimeUnit.SECONDS)
    "CA.kt"
    })
    fun foo(bar: suspend () -> String) {
    launch(CommonPool) { println(bar.invoke()) }
    }

    View Slide

  18. Suspending functions
    10秒後に “CA.kt” と出力される
    foo({
    delay(10, TimeUnit.SECONDS)
    "CA.kt"
    })
    fun foo(bar: suspend () -> String) {
    launch(CommonPool) { println(bar.invoke()) }
    }

    View Slide

  19. Suspending functions

    foo({
    delay(10, TimeUnit.SECONDS)
    "CA.kt"
    })
    fun foo(bar: suspend () -> String) {
    launch(CommonPool) { println(bar.invoke()) }
    }

    View Slide

  20. Coroutine builders (Suspending lambda)

    View Slide

  21. Coroutine builders
    • 引数として(複数の)Suspending Lambdaを取ってコルーチンを作成し、
    オプションでその結果へのアクセスを与える関数

    View Slide

  22. Coroutine builders
    Name Result Scope Description
    launch Job CoroutineScope Launches coroutine that does not have any result
    async Deferred CoroutineScope Returns a single value with the future result
    produce ProducerJob ProducerScope Produces a stream of elements
    actor ActorJob ActorScope Processes a stream of messages
    runBlocking T CoroutineScope Blocks the thread while the coroutine runs
    IUUQTHJUIVCDPN,PUMJOLPUMJOYDPSPVUJOFT

    View Slide

  23. launch
    結果を返さない(Unit)コルーチンを作る
    public fun launch(
    context: CoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
    ): Job {

    View Slide

  24. launch
    • 第一引数のCoroutineContextはスレッドの様な実行環境を指定する(次節で解説)
    • 第二引数のCoroutineStartはコルーチンを開始するタイミングを指定できる
    public fun launch(
    context: CoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
    ): Job {

    View Slide

  25. async
    結果を返すコルーチンを作る
    public fun async(
    context: CoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> T
    ): Deferred {

    View Slide

  26. launchとasyncの違い
    • launchはJobを返す
    • asyncはDeferredを返す
    • DeferredはJobのinterface
    public interface Deferred : Job {
    val isCompletedExceptionally: Boolean
    val isCancelled: Boolean
    public suspend fun await(): T
    public fun registerSelectAwait(select: SelectInstance,
    block: suspend (T) -> R)
    public fun getCompleted(): T
    }

    View Slide

  27. Others (Coroutine builders)
    • 入門としてはlaunchとasyncが使えれば問題ない
    • produce, actorはgoroutineのようなChannelの概念がある

    produce: sender, actor: receiver
    • runBlockingはその名の通りスレッドを止めて動作する
    cf: https://github.com/Kotlin/kotlinx.coroutines/blob/master/ui/coroutines-guide-ui.md

    View Slide

  28. Coroutine dispatchers (Coroutine context)

    View Slide

  29. Coroutine dispatchers
    Name Description
    CommonPool Confines coroutine execution to a shared pool of threads
    newSingleThreadContext Create new single-threaded coroutine context
    newFixedThreadPoolContext Creates new thread pool of a fixed size
    Executor.asCoroutineDispatcher Extension to convert any executor
    Unconfined Does not confine coroutine execution in any way
    IUUQTHJUIVCDPN,PUMJOLPUMJOYDPSPVUJOFT

    View Slide

  30. CommonPool
    java.util.concurrent.ForkJoinPoolを使って

    スレッドプールを作成して良い感じにやってくれる
    launch(CommonPool) { }

    View Slide

  31. newSingleThreadContext
    thread poolが1のCoroutine Dispatcherを作成する
    launch(newSingleThreadContext("thread name")) { }

    View Slide

  32. newFixedThreadPoolContext
    • newSingleThreadContextのthread pool数を指定できる版
    • ソースの中身は一緒
    launch(newFixedThreadPoolContext(3, "thread name")) { }

    View Slide

  33. asCoroutineDispatcher
    java.util.concurrentのExecutorをCoroutine Contextに変換出来る
    class MyCoroutineExecutor : Executor {
    override fun execute(command: Runnable?) { }
    }
    val myCoroutineExecutor = MyCoroutineExecutor()
    launch(myCoroutineExecutor.asCoroutineDispatcher()) { }

    View Slide

  34. Unconfined
    • 特定のスレッドに依らないCoroutine Context
    • コルーチンを再開後に特定のスレッドかスレッドプールに限定する必要が
    あるが、最初の中断まで現在の呼び出しスレッドで実行したい場合は、前
    述のCoroutineBuilderにCoroutineStart.UNDISPATCHEDを設定する
    launch(Unconfined) { }

    View Slide

  35. How to use a Coroutine

    View Slide

  36. さっき読めなかったやつ

    foo({
    delay(10, TimeUnit.SECONDS)
    "CA.kt"
    })
    fun foo(bar: suspend () -> String) {
    launch(CommonPool) { println(bar.invoke()) }
    }

    View Slide

  37. おさらい
    Kotlinはメソッドの最後の引数が関数の場合、カッコの外に書ける

    上と下のhogeメソッド呼び出しは一緒の意味
    hoge(1, { })
    hoge(1) {
    }
    fun hoge(foo: Int, bar: () -> Unit) {
    }

    View Slide

  38. おさらい
    第二引数はデフォルト引数が設定されているので、

    呼び出し時に指定しなくても良い
    public fun launch(
    context: CoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit
    ): Job {

    View Slide

  39. さっき読めなかったやつ
    foo({
    delay(10, TimeUnit.SECONDS)
    "CA.kt"
    })
    fun foo(bar: suspend () -> String) {
    launch(CommonPool) { println(bar.invoke()) }
    }
    引数に取ったbar suspend関数を実行するコルーチンを

    良い感じのスレッドで実行して結果を出力

    View Slide

  40. Example 1
    suspend fun foo(): String {
    println("---foo--- : ${Thread.currentThread().name}")
    delay(3, TimeUnit.SECONDS)
    return "CA.kt"
    }

    View Slide

  41. Example 1-1
    fun main() {
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    println(foo())
    println("----2---- : ${Thread.currentThread().name}")
    }
    println("----3---- : ${Thread.currentThread().name}”)
    }

    View Slide

  42. Example 1-1
    ----0---- : main
    ----3---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1

    ---foo--- : ForkJoinPool.commonPool-worker-1
    CA.kt
    ----2---- : ForkJoinPool.commonPool-worker-1
    fun main() {
    println("----0----- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1----- : ${Thread.currentThread().name}")
    println(foo())
    println("----2----- : ${Thread.currentThread().name}")
    }
    println("----3----- : ${Thread.currentThread().name}”)
    }

    View Slide

  43. Example 1-1
    ----0---- : main
    ----3---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1

    ---foo--- : ForkJoinPool.commonPool-worker-1
    CA.kt
    ----2---- : ForkJoinPool.commonPool-worker-1
    ※ Threadの数字は実行環境によって変わるので、気にしなくても大丈夫です

    View Slide

  44. Example 1-2
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----3---- : ${Thread.currentThread().name}")
    }
    println("----4---- : ${Thread.currentThread().name}")

    View Slide

  45. Example 1-2
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----3---- : ${Thread.currentThread().name}")
    }
    println("----4---- : ${Thread.currentThread().name}")
    Deferred.await() => 実行を中断して値を返すsuspend関数

    View Slide

  46. Example 1-2
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----3---- : ${Thread.currentThread().name}")
    }
    println("----4---- : ${Thread.currentThread().name}")
    ----0---- : main
    ----4---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1
    ----2---- : ForkJoinPool.commonPool-worker-1

    ---foo--- : ForkJoinPool.commonPool-worker-1
    CA.kt
    ----3---- : ForkJoinPool.commonPool-worker-1

    View Slide

  47. Example 1-3
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    foo.join()
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.getCompleted())
    println("----4---- : ${Thread.currentThread().name}")
    }
    println("----5---- : ${Thread.currentThread().name}")

    View Slide

  48. Example 1-3
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    foo.join()
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.getCompleted())
    println("----4---- : ${Thread.currentThread().name}")
    }
    println("----5---- : ${Thread.currentThread().name}")
    Job.join() => await同様coroutineの実行完了まで待つが戻り値が無い

    View Slide

  49. Example 1-3
    ----0---- : main
    ----5---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1
    ----2---- : ForkJoinPool.commonPool-worker-1
    ---foo--- : ForkJoinPool.commonPool-worker-2
    ----3---- : ForkJoinPool.commonPool-worker-1
    CA.kt
    ----4---- : ForkJoinPool.commonPool-worker-1
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    foo.join()
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.getCompleted())
    println("----4---- : ${Thread.currentThread().name}")
    }
    println("----5---- : ${Thread.currentThread().name}")

    View Slide

  50. Example 2
    suspend fun foo(): String {
    println("---foo--- : ${Thread.currentThread().name}")
    delay(6, TimeUnit.SECONDS)
    return "foo"
    }
    suspend fun bar(): String {
    println("---bar--- : ${Thread.currentThread().name}")
    delay(3, TimeUnit.SECONDS)
    return "bar"
    }

    View Slide

  51. Example 2-1
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }.await()
    println("----2---- : ${Thread.currentThread().name}")
    val bar = async(CommonPool) { bar() }.await()
    println("----3---- : ${Thread.currentThread().name}")
    println(foo)
    println("----4---- : ${Thread.currentThread().name}")
    println(bar)
    println("----5---- : ${Thread.currentThread().name}")
    }
    println("----6---- : ${Thread.currentThread().name}")

    View Slide

  52. Example 2-1
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }.await()
    println("----2---- : ${Thread.currentThread().name}")
    val bar = async(CommonPool) { bar() }.await()
    println("----3---- : ${Thread.currentThread().name}")
    println(foo)
    println("----4---- : ${Thread.currentThread().name}")
    println(bar)
    println("----5---- : ${Thread.currentThread().name}")
    }
    println("----6---- : ${Thread.currentThread().name}")
    ----0---- : main
    ----6---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1
    ---foo--- : ForkJoinPool.commonPool-worker-2
    ----2---- : ForkJoinPool.commonPool-worker-1
    ---bar--- : ForkJoinPool.commonPool-worker-1
    ----3---- : ForkJoinPool.commonPool-worker-1
    foo
    ----4---- : ForkJoinPool.commonPool-worker-1
    bar
    ----5---- : ForkJoinPool.commonPool-worker-1

    View Slide

  53. Example 2-1
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }.await()
    println("----2---- : ${Thread.currentThread().name}")
    val bar = async(CommonPool) { bar() }.await()
    println("----3---- : ${Thread.currentThread().name}")
    println(foo)
    println("----4---- : ${Thread.currentThread().name}")
    println(bar)
    println("----5---- : ${Thread.currentThread().name}")
    }
    println("----6---- : ${Thread.currentThread().name}")
    ----0---- : main
    ----6---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1
    ---foo--- : ForkJoinPool.commonPool-worker-1
    ----2---- : ForkJoinPool.commonPool-worker-1
    ---bar--- : ForkJoinPool.commonPool-worker-1
    ----3---- : ForkJoinPool.commonPool-worker-1
    foo
    ----4---- : ForkJoinPool.commonPool-worker-1
    bar
    ----5---- : ForkJoinPool.commonPool-worker-1
    ඵ͔͔Δ ௚ྻ࣮ߦ

    View Slide

  54. Example 2-2
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    val bar = async(CommonPool) { bar() }
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----4---- : ${Thread.currentThread().name}")
    println(bar.await())
    println("----5---- : ${Thread.currentThread().name}")
    }
    println("----6---- : ${Thread.currentThread().name}")

    View Slide

  55. Example 2-2
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    val bar = async(CommonPool) { bar() }
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----4---- : ${Thread.currentThread().name}")
    println(bar.await())
    println("----5---- : ${Thread.currentThread().name}")
    }
    println("----6---- : ${Thread.currentThread().name}")
    ----0---- : main
    ----6---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1
    ----2---- : ForkJoinPool.commonPool-worker-1
    ----3---- : ForkJoinPool.commonPool-worker-1
    ---foo--- : ForkJoinPool.commonPool-worker-2
    ---bar--- : ForkJoinPool.commonPool-worker-1
    foo
    ----4---- : ForkJoinPool.commonPool-worker-1
    bar
    ----5---- : ForkJoinPool.commonPool-worker-1

    View Slide

  56. Example 2-2
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) { foo() }
    println("----2---- : ${Thread.currentThread().name}")
    val bar = async(CommonPool) { bar() }
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----4---- : ${Thread.currentThread().name}")
    println(bar.await())
    println("----5---- : ${Thread.currentThread().name}")
    }
    println("----6---- : ${Thread.currentThread().name}")
    ----0---- : main
    ----6---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1
    ----2---- : ForkJoinPool.commonPool-worker-1
    ----3---- : ForkJoinPool.commonPool-worker-1
    ---foo--- : ForkJoinPool.commonPool-worker-2
    ---bar--- : ForkJoinPool.commonPool-worker-1
    foo
    ----4---- : ForkJoinPool.commonPool-worker-1
    bar
    ----5---- : ForkJoinPool.commonPool-worker-1
    ඵͰऴΘΔ ฒྻॲཧ

    View Slide

  57. Example 3 @Android
    Clickしたら3秒後にtext viewにCA.ktをsetする
    val button: Button
    val textView: TextView
    suspend fun foo(): String {
    delay(3, TimeUnit.SECONDS)
    return "CA.kt"
    }
    fun main() {

    button.setOnClickListener {
    launch(CommonPool) {
    textView.text = foo()
    }
    }
    }

    View Slide

  58. Example 3 @Android
    Only the original thread that created a view hierarchy can touch its views.

    View Slide

  59. Example 3 @Android
    Main thread以外でViewの更新は出来ない
    val button: Button
    val textView: TextView
    suspend fun foo(): String {
    delay(3, TimeUnit.SECONDS)
    return "CA.kt"
    }
    fun main() {

    button.setOnClickListener {
    launch(CommonPool) {
    textView.text = foo()
    }
    }
    }

    View Slide

  60. Example 3 @Android
    // for core
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-core:$kotlinCoroutinesVersion”
    // for JDK8
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:$kotlinCoroutinesVersion”
    // for Asynchronous IO on JDK7+
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-nio:$kotlinCoroutinesVersion"
    // for RxJava2
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-rx2:$kotlinCoroutinesVersion"
    // for Android
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-android:$kotlinCoroutinesVersion"
    // for Swing
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-swing:$kotlinCoroutinesVersion"
    // for JavaFX
    compile “org.jetbrains.kotlinx:kotlinx-coroutines-javafx:$kotlinCoroutinesVersion"
    "OESPJE༻ʹ$PSPVUJOFEJTQBUDIFS͕༻ҙ͞Ε͍ͯΔ

    View Slide

  61. Example 3 @Android
    val UI = HandlerContext(Handler(Looper.getMainLooper()), "UI")
    fun Handler.asCoroutineDispatcher() = HandlerContext(this)
    public class HandlerContext(private val handler: Handler, private val name: String? = null) : CoroutineDispatcher(), Delay {
    override fun dispatch(context: CoroutineContext, block: Runnable) {
    handler.post(block)
    }
    override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation) {
    handler.postDelayed({
    with(continuation) { resumeUndispatched(Unit) }
    }, unit.toMillis(time))
    }
    override fun invokeOnTimeout(time: Long, unit: TimeUnit, block: Runnable): DisposableHandle {
    handler.postDelayed(block, unit.toMillis(time))
    return object : DisposableHandle {
    override fun dispose() {
    handler.removeCallbacks(block)
    }
    }
    }
    override fun toString() = name ?: handler.toString()
    override fun equals(other: Any?): Boolean = other is HandlerContext && other.handler === handler
    override fun hashCode(): Int = System.identityHashCode(handler)
    }

    View Slide

  62. Example 3 @Android
    val button: Button
    val textView: TextView
    suspend fun foo(): String {
    delay(3, TimeUnit.SECONDS)
    return "CA.kt"
    }
    fun main() {

    button.setOnClickListener {
    launch(UI) {
    textView.text = foo()
    }
    }
    }
    Coroutine contextの引数をUIに置き換えると想定通りの動作

    View Slide

  63. Example 4 @RxJava
    RxJavaのBehaviorProcessorにある値を取得して出力
    val fooProcessor = BehaviorProcessor.createDefault("CA.kt")
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) {
    fooProcessor
    .doOnNext { println("----2---- : ${Thread.currentThread().name}") }
    .awaitFirst()
    }
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----4---- : ${Thread.currentThread().name}”)
    }
    println("----5---- : ${Thread.currentThread().name}")

    View Slide

  64. Example 4 @RxJava
    awaitFirst() => Rxのstreamからthreadをblockせずに

            値をとってくるsuspend関数
    val fooProcessor = BehaviorProcessor.createDefault("CA.kt")
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) {
    fooProcessor
    .doOnNext { println("----2---- : ${Thread.currentThread().name}") }
    .awaitFirst()
    }
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----4---- : ${Thread.currentThread().name}")
    }
    println("----5---- : ${Thread.currentThread().name}")

    View Slide

  65. Example 4 @RxJava
    ----0---- : main
    ----5---- : main
    ----1---- : ForkJoinPool.commonPool-worker-1
    ----3---- : ForkJoinPool.commonPool-worker-1
    ----2---- : ForkJoinPool.commonPool-worker-2
    CA.kt
    ----4---- : ForkJoinPool.commonPool-worker-1
    val fooProcessor = BehaviorProcessor.createDefault("CA.kt")
    println("----0---- : ${Thread.currentThread().name}")
    launch(CommonPool) {
    println("----1---- : ${Thread.currentThread().name}")
    val foo = async(CommonPool) {
    fooProcessor
    .doOnNext { println("----2---- : ${Thread.currentThread().name}") }
    .awaitFirst()
    }
    println("----3---- : ${Thread.currentThread().name}")
    println(foo.await())
    println("----4---- : ${Thread.currentThread().name}”)
    }
    println("----5---- : ${Thread.currentThread().name}")

    View Slide

  66. Conclusion

    View Slide

  67. Conclusion
    • 結構手軽に非同期処理がかける
    • 既存のRxを置き換える必要はない

    FRESH!ではFluxのStoreから取得する場面によって使い分けている
    • 今回は話していないChannelも使いこなせると より便利
    • Experimentalなので、まだ注意が必要

    View Slide

  68. 時間あったら
    • GCはどうなってるか
    • Bytecodeだと同処理されているか

    View Slide

  69. Resource management and GC
    fun sequenceOfLines(fileName: String) = buildSequence {
    BufferedReader(FileReader(fileName)).use {
    while (true) {
    yield(it.readLine() ?: break)
    }
    }
    }
    fun main(args: Array) {
    sequenceOfLines("examples/sequence/sequenceOfLines.kt")
    .forEach(::println)
    }
    DGIUUQTHJUIVCDPN,PUMJOLPUMJODPSPVUJOFT

    View Slide

  70. Resource management and GC
    • コルーチンの参照を失った場合でも最終的にはGCされる

    => GCによって収集されるまでファイルは開いたままになる
    fun main(args: Array) {
    sequenceOfLines(“examples/sequence/sequenceOfLines.kt")
    .take(3)
    .forEach(::println)
    }

    View Slide

  71. Bytecode
    • 2つの中断があるコルーチン
    • 状態
    A. 1つ目の中断の前
    B. 1つ目の中断後
    C. 2つ目の中断後
    val a = a()
    val y = foo(a).await() // suspension point #1
    b()
    val z = bar(a, y).await() // suspension point #2
    c(z)

    View Slide

  72. class extends CoroutineImpl<...> implements Continuation {
    // The current state of the state machine
    int label = 0
    // local variables of the coroutine
    A a = null
    Y y = null
    void resume(Object data) {
    if (label == 0) goto L0
    if (label == 1) goto L1
    if (label == 2) goto L2
    else throw IllegalStateException()
    L0:
    // data is expected to be `null` at this invocation
    a = a()
    label = 1
    data = foo(a).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L1:
    // external code has resumed this coroutine passing the result of .await() as data
    y = (Y) data
    b()
    label = 2
    data = bar(a, y).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L2:
    // external code has resumed this coroutine passing the result of .await() as data
    Z z = (Z) data
    c(z)
    label = -1 // No more steps are allowed
    return
    }
    }

    View Slide

  73. class extends CoroutineImpl<...> implements Continuation {
    // The current state of the state machine
    int label = 0
    // local variables of the coroutine
    A a = null
    Y y = null
    void resume(Object data) {
    if (label == 0) goto L0
    if (label == 1) goto L1
    if (label == 2) goto L2
    else throw IllegalStateException()
    L0:
    // data is expected to be `null` at this invocation
    a = a()
    label = 1
    data = foo(a).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L1:
    // external code has resumed this coroutine passing the result of .await() as data
    y = (Y) data
    b()
    label = 2
    data = bar(a, y).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L2:
    // external code has resumed this coroutine passing the result of .await() as data
    Z z = (Z) data
    c(z)
    label = -1 // No more steps are allowed
    return
    }
    }

    View Slide

  74. class extends CoroutineImpl<...> implements Continuation {
    // The current state of the state machine
    int label = 0
    // local variables of the coroutine
    A a = null
    Y y = null
    void resume(Object data) {
    if (label == 0) goto L0
    if (label == 1) goto L1
    if (label == 2) goto L2
    else throw IllegalStateException()
    L0:
    // data is expected to be `null` at this invocation
    a = a()
    label = 1
    data = foo(a).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L1:
    // external code has resumed this coroutine passing the result of .await() as data
    y = (Y) data
    b()
    label = 2
    data = bar(a, y).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L2:
    // external code has resumed this coroutine passing the result of .await() as data
    Z z = (Z) data
    c(z)
    label = -1 // No more steps are allowed
    return
    }
    }

    View Slide

  75. class extends CoroutineImpl<...> implements Continuation {
    // The current state of the state machine
    int label = 0
    // local variables of the coroutine
    A a = null
    Y y = null
    void resume(Object data) {
    if (label == 0) goto L0
    if (label == 1) goto L1
    if (label == 2) goto L2
    else throw IllegalStateException()
    L0:
    // data is expected to be `null` at this invocation
    a = a()
    label = 1
    data = foo(a).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L1:
    // external code has resumed this coroutine passing the result of .await() as data
    y = (Y) data
    b()
    label = 2
    data = bar(a, y).await(this) // 'this' is passed as a continuation
    if (data == COROUTINE_SUSPENDED) return // return if await had suspended execution
    L2:
    // external code has resumed this coroutine passing the result of .await() as data
    Z z = (Z) data
    c(z)
    label = -1 // No more steps are allowed
    return
    }
    }

    View Slide