Slide 1

Slide 1 text

Kotlin Coroutine入門 AAkira @ CA.kt

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

@_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

Slide 4

Slide 4 text

@_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')

Slide 5

Slide 5 text

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) • もちろんコルーチンも一部で導入済み

Slide 6

Slide 6 text

私と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

Slide 7

Slide 7 text

About coroutine

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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"

Slide 10

Slide 10 text

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Ͱ։ൃͰඞཁͳϥΠϒϥϦ

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

Suspending functions

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Coroutine builders (Suspending lambda)

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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 }

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

Coroutine dispatchers (Coroutine context)

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

How to use a Coroutine

Slide 36

Slide 36 text

さっき読めなかったやつ foo({ delay(10, TimeUnit.SECONDS) "CA.kt" }) fun foo(bar: suspend () -> String) { launch(CommonPool) { println(bar.invoke()) } }

Slide 37

Slide 37 text

おさらい Kotlinはメソッドの最後の引数が関数の場合、カッコの外に書ける
 上と下のhogeメソッド呼び出しは一緒の意味 hoge(1, { }) hoge(1) { } fun hoge(foo: Int, bar: () -> Unit) { }

Slide 38

Slide 38 text

おさらい 第二引数はデフォルト引数が設定されているので、
 呼び出し時に指定しなくても良い public fun launch( context: CoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job {

Slide 39

Slide 39 text

さっき読めなかったやつ foo({ delay(10, TimeUnit.SECONDS) "CA.kt" }) fun foo(bar: suspend () -> String) { launch(CommonPool) { println(bar.invoke()) } } 引数に取ったbar suspend関数を実行するコルーチンを
 良い感じのスレッドで実行して結果を出力

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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の数字は実行環境によって変わるので、気にしなくても大丈夫です

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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関数

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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の実行完了まで待つが戻り値が無い

Slide 49

Slide 49 text

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

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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 ඵ͔͔Δ ௚ྻ࣮ߦ

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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 ඵͰऴΘΔ ฒྻॲཧ

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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͕༻ҙ͞Ε͍ͯΔ

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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に置き換えると想定通りの動作

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

Conclusion

Slide 67

Slide 67 text

Conclusion • 結構手軽に非同期処理がかける • 既存のRxを置き換える必要はない
 FRESH!ではFluxのStoreから取得する場面によって使い分けている • 今回は話していないChannelも使いこなせると より便利 • Experimentalなので、まだ注意が必要

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

Resource management and GC • コルーチンの参照を失った場合でも最終的にはGCされる
 => GCによって収集されるまでファイルは開いたままになる fun main(args: Array) { sequenceOfLines(“examples/sequence/sequenceOfLines.kt") .take(3) .forEach(::println) }

Slide 71

Slide 71 text

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)

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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