In the name of God
Operation Flow
I am
Seyed Jafari
Senior Software Engineer @ Revolut Ltd
worldsnas.com @worldsnas
My sincere condolences on the occasion of
Imam Hussain Arbaeen
What is Flow
Docs: An asynchronous data stream that
sequentially emits values and completes
normally or with an exception.
Everything is a stream
Image from reactiveStreams:
https://gist.github.com/staltz/868e7e9bc2a7b8c1f754
val myFlow = flowOf("1")
val myFlow = flowOf("1")
myFlow.collect {
println(it)
}
suspend fun run() {}
CoroutineScope
scope.launch {
run()
}
Coroutines
Flow functions/operator types
● Builders/Creators
● Intermediate
● Terminal
1. Builders/Creators
1. Builders/Creators
● flowOf<String>(“first”)
● emptyFlow<String>()
● flow<String> {}
● callbackFlow<String> {}
● channelFlow<String> {}
● listOf<String>().asFlow()
● listOf<Flow<String>>().merge()
channelFlow<String> {} / callbackFlow<String> {}
callbackFlow<String> {
val listener : object : Listener{
override fun onSuccess(value : String){
sendBlocking(value)
channel.close()
}
}
locationManager.register(listener)
awaitClose {
locationManager.unRegister(listener)
}
}
2. Intermediate
2. Intermediate
flowOf("1")
.filter { it.isNotBlank() }
.withIndex()
.map { it.value }
.drop(1)
.take(1)
.buffer(10)
.debounce(10000)
.distinctUntilChanged()
.onStart { }
.onEach { }
.onCompletion { }
.onEmpty { }
.catch { }
.retry(1)
.flowOn()
.transform<String, String> { }
.zip(flowOf("")) { s, s2 -> s }
.combine(flowOf("")) { a: String , b: String -> "" }
.flatMapMerge<String, String> { value -> flowOf(value) }
.flatMapConcat { value -> flowOf(value) }
.flatMapLatest { flowOf(it) }
.scan("") { accumulator: String , value: String -> accumulator + value }
.runningReduce { accumulator , value -> "" }
Intermediates - part 1
● flowOf("1", "", "2").filter { it.isNotBlank() } // "1", "2"
● flowOf(1, 2).map { "$it" } // "1", "2"
● flowOf("first", "second").withIndex()
// "index= 0, value= first", "index=1, value= second"
● flowOf(1, 2, 3).drop(1) // 2, 3
● flowOf(1, 2, 3).take(1) // 1
● flowOf(1, 1, 1, 2, 1, 3).distinctUntilChanged()
// 1, 2, 1, 3
flowOf(1, 2, 3)
.onEach { println("onEach= $it") }
.buffer(10, BufferOverflow.SUSPEND)
.collect {
delay(1000)
println("collected= $it")
}
// onEach= 1, onEach= 2, onEach= 3, collected= 1, collected= 2,
collected= 3
flow {
emit(1)
delay(50)
emit(2)
delay(150)
emit(3)
}.debounce(100)
// 2, 3
Intermediates - part 2
flowOf(1, 2, 3)
.onStart { emit(4) }
.onStart { emit(5) }
//5, 4, 1, 2, 3
flowOf(1, 2, 3).onEach {
println(it)
delay(1000)
}
// 1, 2, 3
flowOf<Int>(1, 2,).filter{ it > 5 }.onEmpty {
emitAll(flowOf(1, 2, 3))
}
//1, 2, 3
flowOf(1, 2, 3).onCompletion { throwable ->
if (throwable == null) {
// normal cancellation
emit(4)
}else{
// exception occurred
// we can not emit anymore
}
}
// 1, 2, 3, 4
flow<Int> {
throw IllegalStateException("cancel the flow")
}
.retryWhen { cause, attempt ->
attempt < 3
}
.catch { throwable ->
emit(1)
}
// 1,
Intermediates - part 3
flowOf(1, 2, 3)
.flowOn(Dispatchers.Default)
.onEach { }
.flowOn(Dispatchers.IO)
// 1, 2, 3
Intermediates - part 4
flowOf("first", "", "second")
.transform {
if(it.isEmpty()) return@transform
val newItems = api.search(it)
emit(newItems)
}
fun Flow<T>.transform(transform: suspend
FlowCollector<R>.(value: T) -> Unit
): Flow<R> = flow {
collect { value ->
return@collect transform(value)}}
api1.get().zip(api2.get()) { f: Response1, s: Response2 ->
f + s
}
api.fetchBookList()
.combine(queryNameFlow){books: List<Book>, s: String ->
books.filter { it.name.contains(s) }
}
flatMap*
● flatMapMerge
● flatMapConcat
● flatMapLatest
flowOf(1, 2, 3).flatMapMerge { value ->
flow {
emit("a$value")
delay(Random.nextLong(1000))
emit("b$value")
}
}
// a1, a2, a3, ?
flowOf(1, 2, 3).flatMapConcat { value ->
flow {
emit("a$value")
delay(Random.nextLong(1000))
emit("b$value")
}
}
// a1, b1, a2, b2,
userGenerateQuery.flatMapLatest { query ->
localDataBase.observeForQuery(query)
}
flowOf(1, 2, 3).flatMapLatest { value ->
flow {
delay(50)
emit("a$value")
delay(Random.nextLong(1000))
emit("b$value")
}
}
// a3, b3
Intermediates - part 5
Reducers
● runningReduce
● scan
flowOf(1, 2, 3)
.runningReduce { accumulator, value ->
accumulator * value
}
// 1, 2, 6
resultFlow
.scan(InitialState) { lastState, newResult ->
lastState.createNewStateForResult(newResult)
}
3. Terminals
3. Terminals
● flowOf(1, 2, 3).collect { }
● flowOf(1, 2, 3).collectIndexed { index, value -> }
● flowOf(1, 2, 3).launchIn(scope)
● flowOf(1, 2, 3).toList()
● flowOf(1, 3, 3).toSet()
● flowOf(1, 2, 3).single()
● flowOf(1, 2, 3).first()
● flowOf(1, 2, 3).reduce { acc, value -> acc + value }
● flowOf(1, 2, 3).fold("") { acc, value -> acc + value }
Collectors
● flowOf(1, 2, 3).collect { } // 1, 2, 3
● flowOf(1, 2, 3).collectIndexed { index, value->
} // 0:1, 1:2, 2:3
● flowOf(1, 2, 3).launchIn(scope)
Collections
● flowOf(1, 2, 3).toList() // [1, 2, 3]
● flowOf(1, 3, 3).toSet() // [1, 3]
Firsty
● flowOf(1, 2, 3).single() // throws IllegalStateException
● flowOf(1, 2, 3).first() // 1
Reducers
● flowOf(1, 2, 3).reduce { acc, value ->
acc + value
} //6
● flowOf(1, 2, 3).fold("") { acc, value ->
acc + value
} //"123"
Cold vs Hot flow
Rowing vs Car Engine
Entering hot flows
● (Mutable)SharedFlow
● (Mutable)StateFlow
SharedFlow Builders
● MutableSharedFlow<String>()
● socketFlow.shareIn(scope, SharingStarted.Eagerly)
SharedFlow Emitters
● scope.launch{ mutableSharedFlow.emit() }
● mutableSharedFlow.tryEmit()
StateFlow Builders
● MutableStateFlow<Int>(0)
● flowOfStates.stateIn(scope)
StateFlow Emitters
● scope.launch{ mutableSharedFlow.emit() }
● mutableSharedFlow.tryEmit()
● mutableSharedFlow.value = newValue
Testing
@Test
fun test() = runBlocking {
val events = flowOf(1, 2, 3).toList()
assert(events)
}
Turbine
@Test
fun hotFlowTest() = runBlocking {
val sharedFlow = MutableSharedFlow<Int>()
sharedFlow.test {
sharedFlow.emit(1)
assert(expectItem())
cancelAndIgnoreRemainingEvents()
}}
OSS projects
1. Reyan: https://github.com/islamversity/Reyan
2. FlowMarbles: https://flowmarbles.com
🎇 Operation successful 🎇
Happy Flowing,
Kotlin Slack
@worldsnas
Thanks
We are actively hiring in wide range of positions.
make sure to checkout our openings on:
https://www.revolut.com/careers

Operation Flow @ ChicagoRoboto