Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

注意書き ● 実用面に寄った内容のため、 Kotlin Coroutinesの本来の設計思想や本質的な理解とは少し 異 なる可能性があります ● そういった点も含め、議論のタネとしてお楽しみいただけますと幸いです 本セッションについて

Slide 5

Slide 5 text

Kotlin Coroutinesとは?

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

suspend fun Flow Channel

Slide 10

Slide 10 text

suspend fun Flow Channel

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

suspend fun Flow Channel

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

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

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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から配信

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

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

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

キャンセル

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

suspend fun Flow Channel

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

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

Slide 49

Slide 49 text

特徴 ● 複数の値を送受信可能 ● 送信の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 消費されるため それぞれに配信されない

Slide 50

Slide 50 text

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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

キャンセル

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

配信値 取得方法 配信方法 終了 その他 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 ここまでのまとめ

Slide 60

Slide 60 text

使い分けの検討

Slide 61

Slide 61 text

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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

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

Slide 68

Slide 68 text

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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

全体を通してまとめ 配信値 取得方法 配信方法 終了 その他 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

Slide 83

Slide 83 text

まとめ

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

参考 ● 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