+ All Categories
Home > Documents > 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi...

分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi...

Date post: 21-Jul-2020
Category:
Upload: others
View: 17 times
Download: 0 times
Share this document with a friend
48
5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1
Transcript
Page 1: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

5分でわかるKotlin Coroutines Flow

sys1yagi

Shibuya.apk 2019/08/01

© 2019 Ubie, Inc. 1

Page 2: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

About Me

22

● 八木俊広

● @sys1yagi

● Software engineer at

医療機関向け 業務効率化

AI問診

患者向け 病気推測

Dr.Ubie

Page 3: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc. 3

5分でわかる Kotlin Coroutines Flow

3

● Kotlin Coroutines Flowとは?

● ホットストリームとコールドストリーム

● ホットストリームとリソースのリーク

● 最初のFlow

● Flowの仕組み

● Flowのオペレータ

● Flowを使う(イベントストリーム)

● まとめ

Page 4: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc. 44

Kotlin Coroutines Flowとは?

Page 5: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Kotlin Coroutines Flowとは?

55

Kotlin Coroutines 1.2.0-alpha-2からfeature previewで登場した、コールドストリーム。

Page 6: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Kotlin Coroutinesが提供する機能たち

66

ワンショット戻り値なし launch{}, Job

ワンショット戻り値あり async{}, Deferred

ホットストリーム(複数の値) Channel

Page 7: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Kotlin Coroutinesが提供する機能たち

77

ワンショット戻り値なし launch{}, Job

ワンショット戻り値あり async{}, Deferred

ホットストリーム(複数の値) Channel

コールドストリーム(複数の値) Flow

Page 8: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc. 88

● 受信者の有無に関わらず動作を開始する

ホットストリーム

コールドストリーム

● 受信し始めない限り動作しない

Page 9: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとChannel

99

kotlinx.coroutines.channels.Channelはホットストリーム。

Page 10: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.1010

ホットストリームは

取り扱いに気をつけないと

結構簡単にリークしてしまう

Page 11: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1111

fun TextView.textChangeAsChannel(): ReceiveChannel<String?> { val channel = Channel<String?>(Channel.CONFLATED) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } channel.invokeOnClose { removeTextChangedListener(textWatcher) } return channel}

TextViewの値の変化をChannelで受け取る関数

Page 12: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1212

fun TextView.textChangeAsChannel(): ReceiveChannel<String?> { val channel = Channel<String?>(Channel.CONFLATED) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } channel.invokeOnClose { removeTextChangedListener(textWatcher) } return channel}

TextViewの値の変化をChannelで受け取る関数

最新の値だけバッファする Channel

Page 13: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1313

fun TextView.textChangeAsChannel(): ReceiveChannel<String?> { val channel = Channel<String?>(Channel.CONFLATED) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } channel.invokeOnClose { removeTextChangedListener(textWatcher) } return channel}

TextViewの値の変化をChannelで受け取る関数

androidx.core:core-ktx:1.2.0-alpha02

にある関数。

Page 14: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1414

fun TextView.textChangeAsChannel(): ReceiveChannel<String?> { val channel = Channel<String?>(Channel.CONFLATED) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } channel.invokeOnClose { removeTextChangedListener(textWatcher) } return channel}

TextViewの値の変化をChannelで受け取る関数

Channelがクローズする時に実行し、

リスナーを解除する

Page 15: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1515

lifecycleScope.launch { val onTextChange = edit.textChangeAsChannel() if (!readOnly) { onTextChange.consumeEach { button.isEnabled = !it.isNullOrEmpty() } }}

TextViewの値の変化をChannelで受け取る関数

Page 16: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1616

lifecycleScope.launch { val onTextChange = edit.textChangeAsChannel() if (!readOnly) { onTextChange.consumeEach { button.isEnabled = !it.isNullOrEmpty() } }}

TextViewの値の変化をChannelで受け取る関数

この時点で動き出す

Page 17: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1717

lifecycleScope.launch { val onTextChange = edit.textChangeAsChannel() if (!readOnly) { onTextChange.consumeEach { button.isEnabled = !it.isNullOrEmpty() } }}

TextViewの値の変化をChannelで受け取る関数

trueでonTextChangeを使わない場合でもリスナーは動き続ける

Page 18: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

ホットストリームとリソースのリーク

1818

lifecycleScope.launch { val onTextChange = edit.textChangeAsChannel() if (!readOnly) { onTextChange.consumeEach { button.isEnabled = !it.isNullOrEmpty() } }}

TextViewの値の変化をChannelで受け取る関数

trueでonTextChangeを使わない場合でもリスナーは動き続ける

リーク!!

Page 19: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.1919

そこでコールドストリーム

Kotlin Coroutines Flow

Page 20: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの導入

2020

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.0-RC"

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.0-RC"

APIは1.3.0-RCでStableになった。一部の関数に experimentalが残っている

Page 21: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2121

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

Page 22: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2222

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

Flowを作成する関数

Page 23: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2323

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

ブロック内は

suspend FlowCollector<T>.() -> Unit

Page 24: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2424

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

任意のsuspend関数を呼び出せる

Page 25: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2525

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

値を送信するsuspend関数

Page 26: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2626

GlobalScope.launch { val ints: Flow<Int> = intStream() ints.collect { println(it) }}

Page 27: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2727

GlobalScope.launch { val ints: Flow<Int> = intStream() ints.collect { println(it) }}

この時点で動作はしない

Page 28: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2828

GlobalScope.launch { val ints: Flow<Int> = intStream() ints.collect { println(it) }}

受信を開始するsuspend関数

Page 29: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

最初のFlow

2929

GlobalScope.launch { val ints: Flow<Int> = intStream() ints.collect { println(it) }}

0123456789

Page 30: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの仕組み

3030

GlobalScope.launch { val ints: Flow<Int> = intStream() ints.collect { println(it) }}

public suspend inline fun <T> Flow<T>.collect( crossinline action: suspend (value: T) -> Unit): Unit = collect(object : FlowCollector<T> { override suspend fun emit(value: T) = action(value) })

Page 31: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの仕組み

3131

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

Page 32: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの仕組み

3232

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

public fun <T> flow( @BuilderInference block: suspend FlowCollector<T>.() -> Unit): Flow<T> { return object : Flow<T> { override suspend fun collect(collector: FlowCollector<T>) { SafeCollector(collector, coroutineContext).block() } }}

Page 33: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの仕組み

3333

suspendFlow<T>.collect()

suspendFlow#collect()

SafeCollector( collector, coroutineContext).block()

flow ( block: suspend FlowCollector<T>.() -> Unit)

fun intStream(): Flow<Int> = flow { repeat (10) { delay(10) emit(it) }}

受信開始

Page 34: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの仕組み

3434

● Flow<T>.collect関数をトリガーにして、値を発行するsuspend関数

を実行する

● Flow<T>.collect関数はsuspend関数なので必ずコルーチンスコー

プに属する。そのため閉じ忘れがない

● 受信者毎に独立して動作する

Page 35: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowのオペレータ

3535

val ints: Flow<Int> = intStream()val processed = ints .filter { it % 2 == 0 } .map { it * 2 } .drop(1) .take(3)

GlobalScope.launch { processed.collect { println("$it") }}

Page 36: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowのオペレータ

3636

val ints: Flow<Int> = intStream()val processed = ints .filter { it % 2 == 0 } .map { it * 2 } .drop(1) .take(3)

GlobalScope.launch { processed.collect { println("$it") }}

値を加工するオペレータ、suspend関数じゃないのでどこでも呼び出せる。関数ブロック内はsuspend関数

Page 37: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowのオペレータ

3737

val ints: Flow<Int> = intStream()val processed = ints .filter { it % 2 == 0 } .map { it * 2 } .drop(1) .take(3)

GlobalScope.launch { processed.collect { println("$it") }}

48

12

Page 38: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの末端オペレータ

3838

GlobalScope.launch { val result = processed.first()}

最初の1件を取り出す

Page 39: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowの末端オペレータ

3939

GlobalScope.launch { val result = processed.first()}

collect(): Unitsingle(): TsingleOrNULL(): T?toList(): List<T>count(): Intfold(): Treduce(): T...色々ある

Page 40: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowを使ってイベントストリームを作る

4040

public fun <T> channelFlow( @BuilderInference block: suspend ProducerScope<T>.() -> Unit): Flow<T> = ChannelFlowBuilder(block)

値の送信にChannelを利用するFlowを作る関数

Page 41: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowを使ってイベントストリームを作る

4141

fun TextView.textChangeAsFlow(): Flow<String?> = channelFlow<String?> { this:ProducerScope<T>

channel.offer(text.toString()) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } awaitClose { removeTextChangedListener(textWatcher) } }.conflate()

Page 42: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowを使ってイベントストリームを作る

4242

fun TextView.textChangeAsFlow(): Flow<String?> = channelFlow<String?> { this:ProducerScope<T>

channel.offer(text.toString()) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } awaitClose { removeTextChangedListener(textWatcher) } }.conflate()

プロパティにSendChannel

をもっている

Page 43: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowを使ってイベントストリームを作る

4343

fun TextView.textChangeAsFlow(): Flow<String?> = channelFlow<String?> { this:ProducerScope<T>

channel.offer(text.toString()) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } awaitClose { removeTextChangedListener(textWatcher) } }.conflate()

値を送信

Page 44: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowを使ってイベントストリームを作る

4444

fun TextView.textChangeAsFlow(): Flow<String?> = channelFlow<String?> { this:ProducerScope<T>

channel.offer(text.toString()) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } awaitClose { removeTextChangedListener(textWatcher) } }.conflate()

ブロックを抜けるとChannelが終了する

ので、受信者に閉じられるまで待つ

Page 45: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowを使ってイベントストリームを作る

4545

fun TextView.textChangeAsFlow(): Flow<String?> = channelFlow<String?> { this:ProducerScope<T>

channel.offer(text.toString()) val textWatcher = addTextChangedListener { channel.offer(it?.toString()) } awaitClose { removeTextChangedListener(textWatcher) } }.conflate() 常に最新の値を流す

Page 46: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

Flowを使ってイベントストリームを作る

4646

lifecycleScope.launch { val onTextChange = edit.textChangeAsFlow() if (!readOnly) { onTextChange.collect { button.isEnabled = !it.isNullOrEmpty() } }}

ここで初めて動き出す

Page 47: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

まとめ

4747

● Kotlin CoroutinesにコールドストリームがなかったのでFlowが登場

● Channelはホットストリーム

● ホットストリームは結構簡単にリークする

● Flowは受信を開始しない限り動作しない

● Flowは実行する時に必ずコルーチンスコープに属するので閉じ忘れがない

● Flowにはオペレータがたくさんある

● ChannelはFlowの中で生き続ける

● FlowはだいたいRxJava

● Androidにおいては、RxJavaでストリームを扱っていたものをリプレースする時に使えそう

Page 48: 分でわかるKotlin Coroutines Flow · 5分でわかるKotlin Coroutines Flow sys1yagi Shibuya.apk 2019/08/01 © 2019 Ubie, Inc. 1

© 2019 Ubie, Inc.

細かいところはテクブC96本で...!

4848

https://techbooster.booth.pm/items/1485567


Recommended