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

Job, CoroutineContext, CoroutineScopeなどを
整理したい

Avatar for takahirom takahirom
October 24, 2018
1.8k

Job, CoroutineContext, CoroutineScopeなどを
整理したい

Avatar for takahirom

takahirom

October 24, 2018
Tweet

More Decks by takahirom

Transcript

  1. 最近バージョンのCoroutinesで よくある記述がよくわからない import kotlin.coroutines.* import kotlinx.coroutines.* class Activity : CoroutineScope

    { lateinit var job: Job fun create() { job = Job() } fun destroy() { job.cancel() } // to be continued ... // class Activity continues override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job // to be continued ... // class Activity continues • ドキュメントを読んでいるとライフサイクルの
 ハンドリングの例が出てきます https://github.com/Kotlin/kotlinx.coroutines/blob/4a821135127ba56d4e2a27d3729b3f24e5a7aad7/docs/ coroutine-context-and-dispatchers.md#coroutine-context-and-dispatchers より
  2. import kotlin.coroutines.* import kotlinx.coroutines.* class Activity : CoroutineScope { lateinit

    var job: Job fun create() { job = Job() } fun destroy() { job.cancel() } // to be continued ... // class Activity continues override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job // to be continued ... // class Activity continues fun doSomething() { // launch ten coroutines for a demo, each working for a different time repeat(10) { i -> launch { delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc println("Coroutine $i is done") } } } } // class Activity ends CoroutineScopeを実装
  3. import kotlin.coroutines.* import kotlinx.coroutines.* class Activity : CoroutineScope { lateinit

    var job: Job fun create() { job = Job() } fun destroy() { job.cancel() } // to be continued ... // class Activity continues override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job // to be continued ... // class Activity continues fun doSomething() { // launch ten coroutines for a demo, each working for a different time repeat(10) { i -> launch { delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc println("Coroutine $i is done") } } } } // class Activity ends onCreateとかで jobを作っておき
  4. import kotlin.coroutines.* import kotlinx.coroutines.* class Activity : CoroutineScope { lateinit

    var job: Job fun create() { job = Job() } fun destroy() { job.cancel() } // to be continued ... // class Activity continues override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job // to be continued ... // class Activity continues fun doSomething() { // launch ten coroutines for a demo, each working for a different time repeat(10) { i -> launch { delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc println("Coroutine $i is done") } } } } // class Activity ends 何らかのメソッドで CoroutineScope.launchなどの 拡張関数を使ってコルーチンを スタート
  5. import kotlin.coroutines.* import kotlinx.coroutines.* class Activity : CoroutineScope { lateinit

    var job: Job fun create() { job = Job() } fun destroy() { job.cancel() } // to be continued ... // class Activity continues override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job // to be continued ... // class Activity continues fun doSomething() { // launch ten coroutines for a demo, each working for a different time repeat(10) { i -> launch { delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc println("Coroutine $i is done") } } } } // class Activity ends そのときにこのCoroutineContextが 使われる
  6. import kotlin.coroutines.* import kotlinx.coroutines.* class Activity : CoroutineScope { lateinit

    var job: Job fun create() { job = Job() } fun destroy() { job.cancel() } // to be continued ... // class Activity continues override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job // to be continued ... // class Activity continues fun doSomething() { // launch ten coroutines for a demo, each working for a different time repeat(10) { i -> launch { delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc println("Coroutine $i is done") } } } } // class Activity ends destory()でキャンセルすれば実⾏中 のCoroutineがキャンセルされる
  7. import kotlin.coroutines.* import kotlinx.coroutines.* class Activity : CoroutineScope { lateinit

    var job: Job fun create() { job = Job() } fun destroy() { job.cancel() } // to be continued ... // class Activity continues override val coroutineContext: CoroutineContext get() = Dispatchers.Default + job // to be continued ... // class Activity continues fun doSomething() { // launch ten coroutines for a demo, each working for a different time repeat(10) { i -> launch { delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc println("Coroutine $i is done") } } } } // class Activity ends これ何、、? 何でも⾜せるの、、?
  8. もう⼀つ⾜してみる val job = Job() val default = Dispatchers.Default val

    io = Dispatchers.IO val context = job + default + io print(context)
  9. Dispatchers.Defaultが消えた? val job = Job() val default = Dispatchers.Default val

    io = Dispatchers.IO val context = job + default + io print(context)
  10. val job = Job() val default = Dispatchers.Default val io

    = Dispatchers.IO val context = job + default + io print(context) defaultとio両⽅ContinuationInterceptor.Keyという キーなので、後から⾜されたioが残る
  11. val dispatcher = Dispatchers.Default val context = Job() + dispatcher

    val continuationInterceptor = context[ContinuationInterceptor] println(dispatcher == continuationInterceptor) // true キーを指定して取り出す
  12. • EmptyCoroutineContext - なにもないCoroutineContext、⾜ しても何も起きない val job = Job() val

    context = job + EmptyCoroutineContext print(job == context) // true EmptyCoroutineContext • ⾃分⾃⾝をminusKey()したりすると出てくる val job = Job() val context = job.minusKey(Job.Key) print(EmptyCoroutineContext == context) // true
  13. • ユーザーが新たにContextを作ったりするときに使う。Keyを compnaion objectで⾃分で定義する class AuthUser(val name: String) : AbstractCoroutineContextElement(AuthUser)

    { companion object Key : CoroutineContext.Key<AuthUser> } coroutineContext[AuthUser]ͳͲͰऔΓग़ͤΔ AbstractCoroutineContextElement (͜Εɺɺ࢖͏ɺɺʁ)
  14. object Swing : AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor { override fun <T> interceptContinuation(continuation:

    Continuation<T>): Continuation<T> = SwingContinuation(continuation) } private class SwingContinuation<T>(val cont: Continuation<T>) : Continuation<T> { override val context: CoroutineContext = cont.context override fun resumeWith(result: Result<T>) { SwingUtilities.invokeLater { cont.resumeWith(result) } } } • 実⾏スレッドを決めるもの(例: Dispatchers.Defaultなど) • キーはContinuationInterceptor.Keyを使う必要がある • ⾃分で実装して、任意のスレッドで⾛らせられるようにでき る(コードの説明は省略) ContinuationInterceptor
  15. • キーはCoroutineExceptionHandler.Keyを使う必要がある • Exceptionが起きたときの汎⽤的なハンドリングとして使える • CoroutineExceptionHandler()トップレベル関数を使うとすごく簡 単に使える。 val context =

    Job() + Dispatchers.Default + CoroutineExceptionHandler { coroutineContext, throwable -> println("catch") } CoroutineScope(context).launch { throw RuntimeException("!!!!!") } CoroutineExceptionHandler CoroutineExceptionHandlerは全体で Exception管理するときなどに使える! (Androidの場合、クラッシュも防げる)
  16. public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart =

    CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { val newContext = coroutineContext + context val coroutine = StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine CoroutineScope.launchの中 (簡略化してます) 基本的にcontext = Emptyなので
 CoroutineScopeが持っているCoroutineContextが使われる
  17. public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart =

    CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job { val newContext = coroutineContext + context val coroutine = StandaloneCoroutine(newContext, active = true) coroutine.start(start, coroutine, block) return coroutine CoroutineScope.launchの中 (簡略化してます) newContextを元にStandaloneCoroutineを⽣成している つまりDispatcherを変えたいとかでない限りは 渡す必要ないはず
  18. まとめ • CoroutineContextはサブクラスでいろんな事をやっているイ ンターフェースです • Keyで管理されています • CoroutineContextは⾜すとCombinedContextになります • CombinedContextの中にあるCoroutineContextはキーでいろ

    いろ操作できます • CoroutineExceptionHandlerはアプリ全体でException管理す るときなどに使えます • CoroutineScopeを使っていればlaunchに毎回引数で coroutineContextを渡す必要はないです。