Save 37% off PRO during our Black Friday Sale! »

Start with the kotlin flow

3d47c78737bf639fc934b232198c8256?s=47 Abhishesh
February 06, 2021

Start with the kotlin flow

Flow is a new stream processing API introduced in kotlin.

In this talk we'll learn about flow API's, internal details & how flow can be used to handle asynchronous streams of data.

3d47c78737bf639fc934b232198c8256?s=128

Abhishesh

February 06, 2021
Tweet

Transcript

  1. Start with the kotlin flow Abhishesh Srivastava abhishesh_sri

  2. • Threads • Coroutines

  3. fun doLongRunningWork( callback: (x: Int) -> Unit) { thread {

    val result = compute() callback(result) } }
  4. suspend fun doLongRunningWork : Int() { val result = compute()

    return result } suspend fun compute() : Int { ... }
  5. fun doLongRunningWork( callback: (x: Int) -> Unit) { thread {

    val result = compute() callback(result) } } suspend fun doLongRunningWork : Int() { val result = compute() return result } suspend fun compute() : Int { ... }
  6. doLongRunningWork { doLongRunningWork { ... ... } } couroutineScope.launch {

    val first = doLongRunningWork() val second = doLongRunningWork() ... ... }
  7. doLongRunningWork { doLongRunningWork { doLongRunningWork { ... ... } }

    } couroutineScope.launch { val first = doLongRunningWork() val second = doLongRunningWork() val third = doLongRunningWork() ... ... }
  8. Repository updates UI suspend fun updates() : ??? { ...

    }
  9. • Asynchronous data stream • Built on top of coroutine

    • Emits value • Completes normally OR with exception • Usually flows are cold
  10. None
  11. • Asynchronous data stream • Built on top of coroutine

    • Emits value • Completes normally OR with exception • Usually flows are cold flow<Int> { for (i in 1..10) { emit(i) } }
  12. • Asynchronous data stream • Built on top of coroutine

    • Emits value • Completes normally OR with exception • Usually flows are cold flow<Int> { for (i in 1..10) { println(“emit: $i”) emit(i) } }
  13. • Asynchronous data stream • Built on top of coroutine

    • Emits value • Completes normally OR with exception • Usually flows are cold flow<Int> { for (i in 1..10) { println(“emit: $i”) emit(i) } }.collect { println(“col: $it”) println(it) }
  14. public interface Flow<out T> { public suspend fun collect(collector: FlowCollector<T>)

    } public interface FlowCollector<in T> { public suspend fun emit(value: T) }
  15. flow { emit(value) } flowOf(1, 2, 3) listOf<Int>(1, 2, 3,

    4).asFlow() channelFlow<Int> { ... }
  16. • map • filter • take • zip • buffer

    • conflate • … etc
  17. CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { println("emit:

    $i") emit(i) } }.map { println("map: $it") it * 2 }.collect { println("collect: $it") } }
  18. CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { println("emit:

    $i") emit(i) } }.map { println("map: $it") it * 2 }.collect { println("collect: $it") } } emit: 1 map: 1 collect: 2 emit: 2 map: 2 collect: 4 emit: 3 map: 3 collect: 6
  19. CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { println("emit:

    $i") emit(i) } }.filter { println("filter: $it") it % 2 == 0 }.collect { println("collect: $it") } }
  20. CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { println("emit:

    $i") emit(i) } }.filter { println("filter: $it") it % 2 == 0 }.collect { println("collect: $it") } } emit: 1 filter: 1 emit: 2 filter: 2 collect: 2 emit: 3 filter: 3
  21. CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { delay(100)

    emit(i) } }.collect { delay(200) println("collect: $it") } } Output 1 2 3 Time taken : 937ms
  22. CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { delay(100)

    emit(i) } }.buffer(). collect { delay(200) println("collect: $it") } } Output 1 2 3 Time taken : 745ms
  23. CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { delay(100)

    emit(i) } }.conflate() .collect { delay(300) println("collect: $it") } } Output 1 3
  24. • collect • single • reduce • toList • launchIn

    • … etc
  25. • Context preservation • Exception transparency

  26. CoroutineScope(Dispatchers.Main).launch { flow<Int> { withContext(Dispatchers.IO) { for (i in 1..10)

    { emit(i) } } }.collect { println(it) } }
  27. CoroutineScope(Dispatchers.Main).launch { flow<Int> { withContext(Dispatchers.IO) { for (i in 1..10)

    { emit(i) } } }.collect { println(it) } } java.lang.IllegalStateException: Flow invariant is violated:
  28. CoroutineScope(Dispatchers.Main).launch { flow<Int> { for (i in 1..10) { emit(i)

    } }.flowOn(Dispatchers.IO) .collect { println(it) } } Changes the context of upstream flow
  29. uiScope.launch { try { dataFlow().collect { data -> updateUI(data) }

    } catch (e: Exception) { } }
  30. uiScope.launch { dataFlow() .catch{ e -> println(“caught $e“} .collect {

    value -> println(value) } } a downstream exception must always be propagated to the collector.
  31. Retrofit supports suspend function Integrated into many jetpack libraries Other

    reactive libraries can be easily migrated to Flow
  32. Integrated with room @Dao abstract class Dao { @Query("SELECT *

    FROM table") abstract fun getExamples(): Flow<List<Model>> }
  33. Integrated with live data implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" fun <T> LiveData<T>.asFlow(): Flow<T>

    {} fun <T> Flow<T>.asLiveData( context: CoroutineContext = EmptyCoroutineContext, timeout: Duration ): LiveData<T> { }
  34. • Producer • consumer

  35. class FakeFlowProducer : FlowProducer { fun data() = flow {

    emit(ITEM_1) } fun allData() = listOf(ALL_VALUES).asFlow() } @Test fun fakeFlowProducerTest() = runBlocking { val producer = FakeFlowProducer() val firstItem = producer.data.first() assertThat(firstItem, isEqualTo(ITEM_1) }
  36. class FakeFlowProducer : FlowProducer { fun data() = flow {

    emit(ITEM_1) } fun allData() = listOf(ALL_VALUES).asFlow() } @Test fun fakeFlowProducerTest() = runBlocking { val producer = FakeFlowProducer() val firstItem = producer.data.first() assertThat(firstItem, isEqualTo(ITEM_1) } Creates a testcoroutinescope which have aa testcoroutinedispatcher & testcoroutineexceptionhandler. Also accepts and lambda test body.
  37. @Test fun testMultipleValues() = runBlocking { val producer = FakeFlowProducer()

    val multipleValues = producer.multipleValues().toList() assertThat(multipleValues, isEqualTo(ALL_VALUES)) }
  38. producer.allData.drop(1).first() producer.allData.take(5).toList() producer.allData.take(5).toSet() producer.allData.takeWhile(predicate).take(2).toList() producer.allData.firstWhile(predicate) producer.allData.map(transformation).take(5) producer.allData.single() producer.allData.count() producer.allData.count(predicate)

  39. None