As a part of kotlinx.coroutines 1.2 Flow was introduced as a cold asynchronous data stream. How does it work? What we can do with it? Is it that we waiting so long for replacing Rx? The talk has answers to them.
Async in Kotlin // Single value suspend fun foo(p: Params): Value = withContext(Dispatchers.Default) { bar(p) } // Collection of values suspend fun foo(p: Params): List = buildList { while (hasMore) add(nextValue) } // Stream of values fun foo(p: Params): Sequence = sequence { while (hasMore) yield(nextValue) }
Async in Kotlin // Single value suspend fun foo(p: Params): Value = withContext(Dispatchers.Default) { bar(p) } // Collection of values suspend fun foo(p: Params): List = buildList { while (hasMore) add(nextValue) } // Stream of values fun foo(p: Params): Sequence = sequence { while (hasMore) yield(nextValue) } Blocking
Channels fun CoroutineScope.fooProducer(p: Params) : ReceiveChannel { return produce { while (hasMore) send(nextValue) } } val values: ReceiveChannel = fooProducer(p) if (someCondition) { return anotherResult // Oops! Leaked channel } // ... do further work with values ...
Flow is a cold asynchronous data stream that executed sequentially (in the same coroutine) emits values and completes normally or with an exception kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/
Builders // From fixed set of values val flowA = flowOf(1, 2, 3) // From function type () -> T val flowB = { repeat(3) { it + 1 } }.asFlow() // Sequential call to the emit() function val flowC = flow { emit(1) emit(2) emit(3) }
fun Iterator.asFlow(): Flow fun Iterable.asFlow(): Flow fun emptyFlow(): Flow fun Array.asFlow(): Flow fun IntRange.asFlow(): Flow fun Sequence.asFlow(): Flow fun (() -> T).asFlow(): Flow fun flow( block: suspend FlowCollector.() -> Unit ): Flow Builders
• \a]enConcat / \a]enMerge Intermediate Operators * Full list of available operators can be found in official documentation kotlin.github.io/kotlinx.coroutines/kotlinx-coroutines-core/kotlinx.coroutines.flow/-flow/
Preservation flow { withContext(Dispatchers.Default) { emit(longComputation()) } } java.lang.IllegalStateException: Flow invariant is violated: flow was collected in [Context A info], but emission happened in [Context B info]. Please refer to 'flow' documentation or use 'flowOn' instead
Preservation flow { GlobalScope.launch(Dispatchers.IO) // is prohibited withContext(Dispatchers.Default) // is prohibited emit(1) // OK coroutineScope { emit(2) // OK -- still the same coroutine } }
• Context preservation Flow encapsulates its own execution context and never propagates or leaks it downstream • Exception transparency Flow implementations never catch or handle exceptions that occur in downstream Flow Constraints
interface Stream { fun subscribe(callback: Callback) fun unsubscribe(callback: Callback) } interface Callback { fun onNext(item: D) fun onError(error: Throwable) fun onCompleted() } Old ways adapter
fun Observable.toFlow(): Flow fun Single.toFlow(): Flow fun Maybe.toFlow(): Flow fun Flowable.toFlow(): Flow NO FROM THE BOX :) WRITE YOU OWN RxJava2 to Flow
@Dao interface SampleDao { } // Request data and observe changes with RxJava @Query("SELECT * FROM ENTITIES") fun queryEntities(): Observable> Room // Blocking request data @Query("SELECT * FROM ENTITIES") fun queryEntities(): List
@Dao interface SampleDao { } // Request data and observe changes with RxJava @Query("SELECT * FROM ENTITIES") fun queryEntities(): Observable> Room // Blocking request data @Query("SELECT * FROM ENTITIES") fun queryEntities(): List // Request data using Coroutines @Query("SELECT * FROM ENTITIES") suspend fun queryEntities(): List
@Dao interface SampleDao { } // Request data and observe changes with RxJava @Query("SELECT * FROM ENTITIES") fun queryEntities(): Observable> Room // Blocking request data @Query("SELECT * FROM ENTITIES") fun queryEntities(): List // Request data using Coroutines @Query("SELECT * FROM ENTITIES") suspend fun queryEntities(): List // Request data and observe changes using Coroutines @Query("SELECT * FROM ENTITIES") fun queryEntities(): Flow>