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

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

takahirom
October 24, 2018
1.7k

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

takahirom

October 24, 2018
Tweet

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を渡す必要はないです。