They finally arrived - Kotlin Flows!
Are they really great or just good enough?
Does RxJava finally have a true competitor?
Will they be the saviour we all have been waiting for?
1. public interface Flow<out T> { 2. public suspend fun collect(collector: FlowCollector< T>) 3. } 1. public interface FlowCollector< in T> { 2. public suspend fun emit(value: T) 3. }
### Operator fusion Adjacent applications of [channelFlow], [flowOn], [buffer], [produceIn], and [broadcastIn] are always fused so that only one properly configured channel is used for execution. 1. flowOf(1, 2, 3) 2. .flowOn(Dispatchers.IO) 3. .buffer(20) 4. .flowOn(Dispatchers.Main) 5. .count { } < channel-based < channel-based < channel-based all reusing single channel
val flow = flow { 2. for (value in 0..5) { 3. print("out $value") 4. emit(value) 5. } 6. } 7. flow.collect { 8. print("in $it") 9. delay(100) 10. } -> out 0, in 0, out 1, in 1, out 2, in 2, ...
val flow = flow { 2. for (value in 0..5) { 3. print("out $value") 4. emit(value) 5. } 6. } 7. flow.buffer(1).collect { 8. delay(100) 9. print("in $it") 10. } -> out 0, out 1, out 2, in 0, out 3, in 1, out 4, in 2, out 5, ...
val flow = flow { 2. for (value in 0..5) { 3. print("out $value") 4. emit(value) 5. } 6. } 7. flow.buffer(Channel.CONFLATED).collect { 8. delay(100) 9. print("in $it") 10. } -> out 0, out 1, out 2, out 3, out 4, out 5, in 0, in 5
@Dao interface UsersDao { @Query("SELECT * FROM users WHERE id == :id") suspend fun getById(id: String): UserRecord @Query("SELECT * FROM users WHERE id == :id") fun rxTrackById(id: String): Flowable<UserRecord> @Query("SELECT * FROM users WHERE id == :id") fun flowTrackById(id: String): Flow<UserRecord> }
faster? internal class JobCancellationException constructor(...) : CancellationException(...), CopyableThrowable<JobCancellationException> { // ... override fun fillInStackTrace(): Throwable { if (DEBUG) { return super.fillInStackTrace() } /* * In non-debug mode we don't want to have a stacktrace * on every cancellation, parent job reference is enough. * Stacktrace of JCE is not needed most of the time and hurts performance. */ return this } // ... }
functions support • No need to handle cancellation • Easy to write simple extensions Cons: • Much bigger overhead -> much slower (especially for cancellation) • Requires scope to call terminal function • More edge-cases (for ex. context preservation, exception transparency) • Most interesting functions are marked as FlowPreview/ExperimentalCoroutinesApi