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

もう迷わないCoroutines 〜suspend funとChannelとFlow〜

nyafunta9858
December 10, 2022

もう迷わないCoroutines 〜suspend funとChannelとFlow〜

nyafunta9858

December 10, 2022
Tweet

More Decks by nyafunta9858

Other Decks in Programming

Transcript

  1. もう迷わないCoroutines
    〜suspend funとChannelとFlow〜
    Kotlin Fest 2022
    @nyafunta9858

    View Slide

  2. 小林 慶弘 (Yoshihiro Kobayashi) a.k.a nyafunta9858
    ● Mobile Engineer @ Money Forward,Inc.
    ● 趣味:ガジェット集め、ゲーム、カメラ etc…

    View Slide

  3. 本セッションについて
    本セッションの内容
    ● kotlinx.coroutines ver.1.6.4
    ● Kotlin Coroutinesのキホンをおさらい
    ● suspend関数・Flow・Channelの特徴を整理
    ● 利用事例のご紹介・ご提案
    目標
    ● 実装に詰まったときの足掛かりになる
    ● 設計・実装の検討・議論の種となる
    ● これから始めるひとが調査・検討する際の取っ掛かりとなる

    View Slide

  4. 注意書き
    ● 実用面に寄った内容のため、
    Kotlin Coroutinesの本来の設計思想や本質的な理解とは少し

    なる可能性があります
    ● そういった点も含め、議論のタネとしてお楽しみいただけますと幸いです
    本セッションについて

    View Slide

  5. Kotlin Coroutinesとは?

    View Slide

  6. コルーチンとは?
    ● 並行実行のデザインパターンのひとつ
    ● 非同期実行するコードを簡略化
    ● 中断(suspend)と再開(resume)をサポート
    ● スレッドよりも軽量

    View Slide

  7. コルーチンのキホン
    コルーチンの基本(起動)
    ● CoroutineScopeからCoroutine Builderで起動
    ● 中断可能な処理(suspend関数)はCoroutineScope内で呼び出し可能
    コルーチンの基本(終了)
    ● CoroutineScope / CoroutineContext(コルーチンの動作に関する情報を持つ
    ) / Jobからキャ
    ンセル可能
    ● CoroutineScopeはライフサイクルに合わせて自動キャンセル可能
    ○ e.g. AAC ViewModelのviewModelScope

    View Slide

  8. コルーチンのキホン
    ● Kotlin コルーチンを 理解しよう - Speaker Deck
    ● Kotlin コルーチンを 理解しよう 2019 - KotlinFest2019 - - Speaker Deck

    View Slide

  9. suspend fun
    Flow
    Channel

    View Slide

  10. suspend fun
    Flow
    Channel

    View Slide

  11. suspend funの特徴
    ● 単一の値を返却
    ● 時間の掛かる処理をnon-blockingで実行
    ● 値を返却せずに待ち続けることも可能

    View Slide

  12. ● 単一の値を返却
    ● 時間の掛かる処理をnon-blockingで実行
    ● 値を返却せずに待ち続けることも可能
    suspend funの特徴

    View Slide

  13. ← non-blocking・単一の値
    ● 単一の値を返却
    ● 時間の掛かる処理をnon-blockingで実行
    ● 値を返却せずに待ち続けることも可能
    suspend funの特徴

    View Slide

  14. ● 単一の値を返却
    ● 時間の掛かる処理をnon-blockingで実行
    ● 値を返却せずに待ち続けることも可能
    ← 中断し続ける
    suspend funの特徴

    View Slide

  15. 値の取得
    ● CoroutineScope内で呼び出し
    ● 同期処理のような書き味で返り値を取得

    View Slide

  16. ● CoroutineScope内で呼び出し
    ● 同期処理のような書き味で返り値を取得
    値の取得

    View Slide

  17. 値の発行
    ● suspend キーワード
    ● suspendラムダを変数、高階関数に指定可能
    ● suspendCancellableCoroutineで配信

    View Slide

  18. ● suspend キーワード
    ● suspendラムダを変数、高階関数に指定可能
    ● suspendCancellableCoroutineなどで配信
    値の発行

    View Slide

  19. ● suspend キーワード
    ● suspendラムダを変数、高階関数に指定可能
    ● suspendCancellableCoroutineなどで配信
    値の発行
    コールバックをsuspendable
    にできる

    View Slide

  20. suspend fun
    Flow
    Channel

    View Slide

  21. Flowの特徴
    ● 複数の値を受信
    ● Flow / SharedFlow / StateFlowの3種
    ○ Flow:Cold
    ○ SharedFlow / StateFlow:Hot
    ● Hot Flowは完了しない

    View Slide

  22. ● 複数の値を受信
    ● Flow / SharedFlow / StateFlowの3種
    ○ Flow:Cold
    ○ SharedFlow / StateFlow:Hot
    ● Hot Flowは完了しない
    Flowの特徴

    View Slide

  23. ← 購読開始して初めて実行される
    ● 複数の値を受信
    ● Flow / SharedFlow / StateFlowの3種
    ○ Flow:Cold
    ○ SharedFlow / StateFlow:Hot
    ● Hot Flowは完了しない
    Flowの特徴

    View Slide

  24. ← 生成時からアクティブ
    ● 複数の値を受信
    ● Flow / SharedFlow / StateFlowの3種
    ○ Flow:Cold
    ○ SharedFlow / StateFlow:Hot
    ● Hot Flowは完了しない
    Flowの特徴

    View Slide

  25. 値の取得
    Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得

    View Slide

  26. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得

    View Slide

  27. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得
    ↑ collectから再開時に実行される
    並行実行される
    ネストが減って見やすい

    View Slide

  28. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得

    View Slide

  29. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得
    ← 再開され...る?
    ↑ 再開の見通しがたつ

    View Slide

  30. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得
    ← 再開され...る?
    ↑ 再開の見通しがたつ

    View Slide

  31. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得
    ← 再開され...る?
    ↑ 再開の見通しがたつ

    View Slide

  32. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得
    ← 再開され...る?
    ↑ 再開の見通しがたつ
    collect1 0
    collect1 1
    collect1 2
    collect1 3
    collect2 0
    collect2 1
    collect2 2
    collect2 3
    collect1 4
    collect2 4
    collect1 5
    collect2 5
    collect1 6
    collect2 6

    replayCacheから配信

    View Slide

  33. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得
    ← 再開され...る?
    ↑ 再開の見通しがたつ

    View Slide

  34. Flow
    ● collectで継続する値の購読
    ● single / first / lastなどの単一での値取得
    SharedFlow / StateFlow
    ● キャッシュされた値の取得
    値の取得
    定義はいるが
    valueをwrapしてるのみ

    View Slide

  35. 値の発行
    Flow Builder
    ● flow / flowOf / callbackFlow / channelFlow
    Extension
    ● asFlow / asSharedFlow / asStateFlow
    ● sharedIn / stateIn
    Factory
    ● MutableSharedFlow / MutableStateFlow

    View Slide

  36. Flow Builder
    ● flow / flowOf / callbackFlow / channelFlow
    Extension
    ● asFlow / asSharedFlow / asStateFlow
    ● sharedIn / stateIn
    Factory
    ● MutableSharedFlow / MutableStateFlow
    値の発行

    View Slide

  37. Flow Builder
    ● flow / flowOf / callbackFlow / channelFlow
    Extension
    ● asFlow / asSharedFlow / asStateFlow
    ● sharedIn / stateIn
    Factory
    ● MutableSharedFlow / MutableStateFlow
    値の発行

    View Slide

  38. Flow Builder
    ● flow / flowOf / callbackFlow / channelFlow
    Extension
    ● asFlow / asSharedFlow / asStateFlow
    ● sharedIn / stateIn
    Factory
    ● MutableSharedFlow / MutableStateFlow
    値の発行

    View Slide

  39. キャンセル
    flow {} では
    currentCoroutineContext()を
    使う必要がある

    View Slide

  40. キャンセル

    View Slide

  41. キャンセル
    どのCoroutineScopeに属している?

    View Slide

  42. キャンセル
    どのCoroutineScopeに属している?

    View Slide

  43. キャンセル
    これでも終了できる

    View Slide

  44. suspend fun
    Flow
    Channel

    View Slide

  45. 特徴
    ● 複数の値を送受信可能
    ● 送信のSendChannel、受信のReceiveChannel
    ● Channelは生成時から値を保持可能な
    Hot Channel
    ● 配信された値は消費される

    View Slide

  46. 特徴
    ● 複数の値を送受信可能
    ● 送信のSendChannel、受信のReceiveChannel
    ● Channelは生成時から値を保持可能な
    Hot Channel
    ● 配信された値は消費される

    View Slide

  47. 特徴
    ● 複数の値を送受信可能
    ● 送信のSendChannel、受信のReceiveChannel
    ● Channelは生成時から値を保持可能な
    Hot Channel
    ● 配信された値は消費される
    SendChannel ReceiveChannel
    Channel

    View Slide

  48. 特徴
    ● 複数の値を送受信可能
    ● 送信のSendChannel、受信のReceiveChannel
    ● Channelは生成時から値を保持可能な
    Hot Channel
    ● 配信された値は消費される

    View Slide

  49. 特徴
    ● 複数の値を送受信可能
    ● 送信のSendChannel、受信のReceiveChannel
    ● Channelは生成時から値を保持可能な
    Hot Channel
    ● 配信された値は消費される
    channel1 0
    channel2 1
    channel1 2
    channel2 3
    channel1 4
    channel2 5
    channel1 6
    channel2 7
    channel1 8
    channel2 9
    消費されるため
    それぞれに配信されない

    View Slide

  50. 値の取得
    ● consumeEachで継続する値の購読
    ● Operatorのchainは不可
    ● receive / receiveCatchingなどで単一での値取得
    ● Flowとして購読するExtensionあり

    View Slide

  51. 値の取得
    ● consumeEachで継続する値の購読
    ● Operatorのchainは不可
    ● receive / receiveCatchingなどで単一での値取得
    ● Flowとして購読するExtensionあり

    View Slide

  52. 値の取得
    ● consumeEachで継続する値の購読
    ● Operatorのchainは不可
    ● receive / receiveCatchingなどで単一での値取得
    ● Flowとして購読するExtensionあり
    いずれも同じ処理
    要素はfor文から渡ってきている

    View Slide

  53. 値の取得
    ● consumeEachで継続する値の購読
    ● Operatorのchainは不可
    ● receive / receiveCatchingなどで単一での値取得
    ● Flowとして購読するExtensionあり
    単一で値取得

    View Slide

  54. 値の発行
    Factory
    ● Channel
    Channel Builder
    ● producer / actor

    View Slide

  55. 値の発行
    Factory
    ● Channel
    Channel Builder
    ● producer / actor

    View Slide

  56. 値の発行
    Factory
    ● Channel
    Channel Builder
    ● producer / actor

    View Slide

  57. キャンセル

    View Slide

  58. キャンセル
    これらからも終了可能

    View Slide

  59. 配信値 取得方法 配信方法 終了 その他
    suspend
    fun 単一の値(型) 呼び出し
    ・suspendキーワード
    ・suspend ラムダ
    ・suspendCancellationCoroutine / suspendCoroutine
    ・コルーチンのキャンセルに準拠
    Flow
    Data Stream ・collect Flow Builder
    ・flow
    ・flowOf
    ・callbackFlow
    ・channelFlow etc
    ・コルーチンのキャンセルに準拠
    ・送信側のclose
    ・Cold Flow
     - Flow
    ・Hot Flow
     - SharedFlow / StateFlow
     - 一対多
    ・Snapshot
     - replayCache (SharedFlow)
     - value (StateFlow)
    単一の値(型)
    ・first
    ・last
    ・toList etc
    Extension
    ・asFlow
    ・asSharedFlow
    ・asStateFlow etc
    Snapshot ・replayCache
    ・value
    Factory
    ・MutableSharedFlow
    ・MutableStateFlow
    Channel
    Data Stream
    単一の値(型)
    ・consumeEach Channel Factory ・Channel
    ・コルーチンのキャンセルに準拠
    ・ReceiveChannel.cancel
    ・SendChannel.close
    ・Hot Channel
    ・一対一
    - 一度の発行で値は消費
    ・値を配信するための仕組み
    - actor
    単一の値(型) ・receive
    ・receiveCatching etc
    Channel Builder
    ・actor
    ・producer
    ここまでのまとめ

    View Slide

  60. 使い分けの検討

    View Slide

  61. Case.1
    Callbackで値を受け取りたいとき
    気にしたポイント
    ● 値をひとつだけ受け取りたい
    ● キャンセルを考慮したい

    View Slide

  62. Case.1
    Callbackで値を受け取りたいとき
    気にしたポイント
    ● 値をひとつだけ受け取りたい
    ● キャンセルを考慮したい

    View Slide

  63. Case.1
    Callbackで値を受け取りたいとき
    気にしたポイント
    ● 値をひとつだけ受け取りたい
    ● キャンセルを考慮したい
    suspendable + cancellable
    成功時の配信
    エラー時の配信
    キャンセル時の処理

    View Slide

  64. Case.2
    Listenerで値を受け取りたいとき
    気にしたポイント
    ● 値を継続的に受け取りたい
    ● キャンセルを考慮したい

    View Slide

  65. Case.2
    Listenerで値を受け取りたいとき
    callbackFlow?
    or
    channelFlow?

    View Slide

  66. Case.2
    Listenerで値を受け取りたいとき
    callbackFlow?
    or
    channelFlow?

    View Slide

  67. Case.2
    Listenerで値を受け取りたいとき
    isClosedForSendがfalseの場合にthrow

    View Slide

  68. Case.2
    Listenerで値を受け取りたいとき

    View Slide

  69. Case.2
    Listenerで値を受け取りたいとき

    View Slide

  70. Case.3
    通信処理の置き換え

    View Slide

  71. Case.3
    通信処理の置き換え
    RxJava
    Retrofit

    View Slide

  72. Case.3
    通信処理の置き換え
    気にしたポイント
    ● 通信結果の利用場所・扱い
    ● 同時購読者はいない
    ● Retrofitのsuspend funサポート

    View Slide

  73. Case.3
    通信処理の置き換え
    気にしたポイント
    ● 通信結果の利用場所・扱い
    ● 同時購読者はいない
    ● Retrofitのsuspend funサポート
    何かしらの変換処理が
    入ることが多い

    View Slide

  74. Case.3
    通信処理の置き換え
    But what if we don’t need either concurrency or synchronization, but need just
    non-blocking streams of data? We did not have a type for that until recently, so welcome
    Kotlin Flow type that is available for preview starting from kotlinx.coroutines version
    1.2.0-alpha-2:
    https://elizarov.medium.com/cold-flows-hot-channels-d74769805f9

    View Slide

  75. Case.3
    通信処理の置き換え
    気にしたポイント
    ● 通信結果の利用場所・扱い
    ● 同時購読者はいない
    ● Retrofitのsuspend funサポート
    suspendを付けて型を変えるだけ
    kotlinx-coroutinesの
    reactive modulesを利用

    View Slide

  76. Case.3
    通信処理の置き換え
    気にしたポイント
    ● 通信結果の利用場所・扱い
    ● 同時購読者はいない
    ● Retrofitのsuspend funサポート

    View Slide

  77. Case.4
    リクエストと購読を分けたい
    気にしたポイント
    ● 同時購読者の存在
    ● 購読にタイミング差がある

    View Slide

  78. Case.4
    リクエストと購読を分けたい
    気にしたポイント
    ● 同時購読者の存在
    ● 購読にタイミング差がある

    View Slide

  79. Case.4
    リクエストと購読を分けたい
    気にしたポイント
    ● 同時購読者の存在
    ● 購読にタイミング差がある
    値を取ってきた後に購読した
    SubscriberにもreplayCacheで配信
    Cold Flowとして公開

    View Slide

  80. Case.5
    Channelを使いたいとき
    SharedFlowへのreplace

    View Slide

  81. Case.5
    Channelを使いたいとき
    受け付けるcapacityを指定できる

    View Slide

  82. 全体を通してまとめ
    配信値 取得方法 配信方法 終了 その他
    suspend
    fun 単一の値(型) 呼び出し
    ・suspendキーワード
    ・suspend ラムダ
    ・suspendCancellationCoroutine / suspendCoroutine
    ・kotlinx-coroutines:reactive modulesサポート
    ・コルーチンのキャンセルに準拠
    Flow
    Data Stream ・collect Flow Builder
    ・flow
    ・flowOf
    ・callbackFlow:コールバック向き
    ・channelFlow:複数コルーチン向き
    ・reactive modulesサポート etc
    ・コルーチンのキャンセルに準拠
    ・送信側のclose
    ・Cold Flow
     - Flow:同期不要な複数値
    ・Hot Flow
     - SharedFlow / StateFlow
     - 一対多
    ・Snapshot
     - replayCache (SharedFlow)
     - value (StateFlow)
    単一の値(型)
    ・first
    ・last
    ・toList etc
    Extension
    ・asFlow
    ・asSharedFlow
    ・asStateFlow
    ・reactive modulesサポート etc
    Snapshot ・replayCache
    ・value
    Factory
    ・MutableSharedFlow
    ・MutableStateFlow
    Channel
    Data Stream
    単一の値(型)
    ・consumeEach Channel Factory ・Channel
    ・コルーチンのキャンセルに準拠
    ・ReceiveChannel.cancel
    ・SendChannel.close
    ・Hot Channel
    ・一対一
    - 一度の発行で値は消費
    ・値を配信するための仕組み
    - actor
    単一の値(型) ・receive
    ・receiveCatching etc
    Channel Builder
    ・actor:受け付ける値上限を指定
    ・producer

    View Slide

  83. まとめ

    View Slide

  84. まとめ
    ● Kotlin Coroutineのキホンをおさらい
    ● suspend fun ・Flow・Channelのキホンをおさらい
    ● シーンごとの利用方法を検討・ご紹介

    View Slide

  85. 参考
    ● Kotlin コルーチンを 理解しよう - Speaker Deck
    ● Kotlin コルーチンを 理解しよう 2019 - KotlinFest2019 - - Speaker Deck
    ● 5分でわかるKotlin Coroutines Flow - Speaker Deck
    ● Cold flows, hot channels. Kotlin coroutines were missing a… | by Roman Elizarov
    ● Reactive Streams and Kotlin Flows | by Roman Elizarov | Medium
    ● https://youtrack.jetbrains.com/issue/KTIJ-16542/
    ● Kotlin/kotlinx.coroutines
    ● SharedFlow last() never returns · Issue #3275 · Kotlin/kotlinx.coroutines · GitHub
    ● Flow builder does not cooperate for cancellation · Issue #2000 · Kotlin/kotlinx.coroutines · GitHub
    ● https://github.com/Kotlin/kotlinx.coroutines/tree/master/reactive/
    ● https://reactivex.io/documentation/ko/operators/backpressure.html
    ● Coroutines guide | Kotlin
    ● Asynchronous Flow | Kotlin
    ● https://kotlinlang.org/docs/channels.html
    ● Shared mutable state and concurrency | Kotlin
    ● Android での Kotlin コルーチン | Android デベロッパー
    ● Android での Kotlin Flow | Android デベロッパー
    ● StateFlow and SharedFlow | Android Developers
    ● 完全に理解した気になるKotlin Coroutines - Qiita
    ● Coroutines reactive moduleで今日からKotlin Coroutinesに入門する - Qiita
    ● Hot and cold data sources
    ● SharedFlow and StateFlow
    ● Cancellation and timeouts | Kotlin
    ● 詳細CoroutineContext | by Kenji Abe
    ● kotlin coroutinesのFlow, SharedFlow, StateFlowを整理する - Blog - Mori Atsushi
    ● kotlin coroutinesのStateFlowのドキュメントを読み込む - Blog - Mori Atsushi

    View Slide