Slide 1

Slide 1 text

Kotlin Coroutines Flowを 触ってみた話し tomoya0x00 Coroutine ハンズオン by DroidKaigi @ LINE Fukuoka #droidkaigi_roadshow

Slide 2

Slide 2 text

About me tomoya0x00 Twitter, GitHub, Qiita Android, Embedded system, BLE/BT, iOS DroidKaigi staff (since DroidKaigi 2019) DeNA Co., Ltd. Automotive Business Unit.

Slide 3

Slide 3 text

Kotlin Coroutines Flow is 何︖

Slide 4

Slide 4 text

Kotlin Coroutines Flow is 何︖ https://kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines- core/kotlinx.coroutines. ow/ Flow — asynchronous cold stream of elements. RxJavaみたいなもの 最近、kotlinx-coroutines-core:1.3.0でstableになった ただし、⼀部はまだexperimentalだったりpreviewだったり @ExperimentalCoroutinesApi @FlowPreview

Slide 5

Slide 5 text

普通の Kotlin Coroutines との違いは︖ 基本的にCoroutinesはワンショットの⾮同期処理⽤ WebAPI呼び出しとか RxJavaのSingle/Maybe/Completableと同じ⽴ち位置 Flowは何度も値を流すストリーム⽤ 位置情報とか RxJavaのObservable/Flowableと同じ⽴ち位置

Slide 6

Slide 6 text

⾃分が Flow を触るモチベーション

Slide 7

Slide 7 text

⾃分が Flow を触るモチベーション プロダクトのコードにRxJavaとCoroutinesのコードが混在 ストリームだけ、ObservableやFlowableになっている できればCoroutinesに統⼀したい そろそろStableになったし触っておこう ちょうどプロダクトで新規機能開発がある デバイス間通信(シリアル通信)の抽象化 Androidなのか・・・︖

Slide 8

Slide 8 text

簡単な使い⽅

Slide 9

Slide 9 text

簡単な使い⽅ val myFlow = flow { // flow builderの⼀つ emit(1) emit(2) } GlobalScope.launch { myFlow.collect { value -> println("Received $value") // Received 1 // Received 2 } }

Slide 10

Slide 10 text

NGな使い⽅

Slide 11

Slide 11 text

NGな使い⽅ suspend fun hoge() = 2 val myFlow = flow { emit(1) // OK withContext(Dispatchers.IO) { emit(hoge()) // NG(collectする側のコンテキストの強制指定はダメ) } } GlobalScope.launch { myFlow.collect { value -> println("Received $value") } }

Slide 12

Slide 12 text

こうすればOK suspend fun hoge() = 2 val myFlow = flow { emit(1) // OK val result = withContext(Dispatchers.IO) { hoge() } emit(result) // OK } GlobalScope.launch { myFlow.collect { value -> println("Received $value") } }

Slide 13

Slide 13 text

いくつかTipsや困った事

Slide 14

Slide 14 text

いくつかTipsや困った事 Tips collectしている間、動き続けるFlow 困った事 Operetorが少ない ConnectableObservable的なモノが無い ※間違っているのもあるかもなので、気付いたら教えて下さい

Slide 15

Slide 15 text

Tips collectしている間、動き続けるFlow

Slide 16

Slide 16 text

collectしている間、動き続けるFlow シリアル通信で受信したデータをひたすらFlowで流したい シリアル通信のread()はブロッキングI/O val received: Flow = flow { coroutineScope { // coroutineScopeでくくると、 while (isActive) { // isActiveでキャンセルされたことが検知できる val data = withContext(Dispatchers.IO) { read() } emit(data) } } }

Slide 17

Slide 17 text

困った事 Operetorが少ない

Slide 18

Slide 18 text

Operetorが少ない RxJavaに⽐べると、bufferやintervalなどOperatorに不⾜を感じる coroutineなので、⼿続き的に書けば似たようなことは実現できる Operatorに関する issue は多い

Slide 19

Slide 19 text

val isAvailable: Flow = flow { coroutineScope { while (isActive) { val available = withContext(Dispatchers.IO) { var receivedAck = false // 1sec間隔で5回pingして、1回でも応答有れば利⽤可能と判定 repeat(5) { try { delay(1000L) ping() receivedAck = true } catch (e: Exception) { // Do nothing } } receivedAck } emit(available) } } }

Slide 20

Slide 20 text

困った事 ConnectableFlowable的なモノが無い

Slide 21

Slide 21 text

ConnectableFlowable的なモノが無い RxJavaにはConnectableFlowable/Observableがある 複数のSubscriberと、同じストリームを共有できる Flowには同様な仕組みがまだ無い Issueで議論されている真っ最中 Consider sharing a Flow through a ConnectableFlow Flow.share operator 直近はBroadcastChannelを使ってどうにかしている BroadcastChannel.asFlow()で変換してFlowとして公開

Slide 22

Slide 22 text

感想 まだまだOperatorが少ない RxJavaでは宣⾔的に書けていたのが、⼿続き的にかかないといけ ないので思考の切替が必要 でも、逆に⾔うと⼿続き的に書けば実現できちゃうので、そこは 良い 参考になる記事も少ない sys1yagiさんの記事がめっちゃ参考になる owOn (RxJavaで⾔うsubscribeOn) ですらexperimental API変更はありえる

Slide 23

Slide 23 text

結論

Slide 24

Slide 24 text

まだ⼿を出すにはちょっと早いかも︖

Slide 25

Slide 25 text

でも、ぜひ使ってフィードバックしてい きましょう︕

Slide 26

Slide 26 text

ありがとうございました