Kotlin Coroutines Flow を触ってみた話し

F46a37b9f855c245f72a07b04045216a?s=47 Tomoya Miwa
September 14, 2019

Kotlin Coroutines Flow を触ってみた話し

F46a37b9f855c245f72a07b04045216a?s=128

Tomoya Miwa

September 14, 2019
Tweet

Transcript

  1. Kotlin Coroutines Flowを 触ってみた話し tomoya0x00 Coroutine ハンズオン by DroidKaigi @

    LINE Fukuoka #droidkaigi_roadshow
  2. About me tomoya0x00 Twitter, GitHub, Qiita Android, Embedded system, BLE/BT,

    iOS DroidKaigi staff (since DroidKaigi 2019) DeNA Co., Ltd. Automotive Business Unit.
  3. Kotlin Coroutines Flow is 何︖

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

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

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

  8. 簡単な使い⽅

  9. 簡単な使い⽅ val myFlow = flow { // flow builderの⼀つ emit(1)

    emit(2) } GlobalScope.launch { myFlow.collect { value -> println("Received $value") // Received 1 // Received 2 } }
  10. NGな使い⽅

  11. 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") } }
  12. こうすれば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") } }
  13. いくつかTipsや困った事

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

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

  16. collectしている間、動き続けるFlow シリアル通信で受信したデータをひたすらFlowで流したい シリアル通信のread()はブロッキングI/O val received: Flow<ByteArray> = flow { coroutineScope

    { // coroutineScopeでくくると、 while (isActive) { // isActiveでキャンセルされたことが検知できる val data = withContext<ByteArray>(Dispatchers.IO) { read() } emit(data) } } }
  17. 困った事 Operetorが少ない

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

  19. val isAvailable: Flow<Boolean> = flow { coroutineScope { while (isActive)

    { val available = withContext<Boolean>(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) } } }
  20. 困った事 ConnectableFlowable的なモノが無い

  21. ConnectableFlowable的なモノが無い RxJavaにはConnectableFlowable/Observableがある 複数のSubscriberと、同じストリームを共有できる Flowには同様な仕組みがまだ無い Issueで議論されている真っ最中 Consider sharing a Flow through

    a ConnectableFlow Flow.share operator 直近はBroadcastChannelを使ってどうにかしている BroadcastChannel.asFlow()で変換してFlowとして公開
  22. 感想 まだまだOperatorが少ない RxJavaでは宣⾔的に書けていたのが、⼿続き的にかかないといけ ないので思考の切替が必要 でも、逆に⾔うと⼿続き的に書けば実現できちゃうので、そこは 良い 参考になる記事も少ない sys1yagiさんの記事がめっちゃ参考になる owOn (RxJavaで⾔うsubscribeOn)

    ですらexperimental API変更はありえる
  23. 結論

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

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

  26. ありがとうございました