Slide 1

Slide 1 text

Start with the kotlin flow Abhishesh Srivastava abhishesh_sri

Slide 2

Slide 2 text

● Threads ● Coroutines

Slide 3

Slide 3 text

fun doLongRunningWork( callback: (x: Int) -> Unit) { thread { val result = compute() callback(result) } }

Slide 4

Slide 4 text

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

Slide 5

Slide 5 text

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 { ... }

Slide 6

Slide 6 text

doLongRunningWork { doLongRunningWork { ... ... } } couroutineScope.launch { val first = doLongRunningWork() val second = doLongRunningWork() ... ... }

Slide 7

Slide 7 text

doLongRunningWork { doLongRunningWork { doLongRunningWork { ... ... } } } couroutineScope.launch { val first = doLongRunningWork() val second = doLongRunningWork() val third = doLongRunningWork() ... ... }

Slide 8

Slide 8 text

Repository updates UI suspend fun updates() : ??? { ... }

Slide 9

Slide 9 text

● Asynchronous data stream ● Built on top of coroutine ● Emits value ● Completes normally OR with exception ● Usually flows are cold

Slide 10

Slide 10 text

No content

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

public interface Flow { public suspend fun collect(collector: FlowCollector) } public interface FlowCollector { public suspend fun emit(value: T) }

Slide 15

Slide 15 text

flow { emit(value) } flowOf(1, 2, 3) listOf(1, 2, 3, 4).asFlow() channelFlow { ... }

Slide 16

Slide 16 text

● map ● filter ● take ● zip ● buffer ● conflate ● … etc

Slide 17

Slide 17 text

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") } }

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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") } }

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..3) { delay(100) emit(i) } }.conflate() .collect { delay(300) println("collect: $it") } } Output 1 3

Slide 24

Slide 24 text

● collect ● single ● reduce ● toList ● launchIn ● … etc

Slide 25

Slide 25 text

● Context preservation ● Exception transparency

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

CoroutineScope(Dispatchers.Main).launch { flow { for (i in 1..10) { emit(i) } }.flowOn(Dispatchers.IO) .collect { println(it) } } Changes the context of upstream flow

Slide 29

Slide 29 text

uiScope.launch { try { dataFlow().collect { data -> updateUI(data) } } catch (e: Exception) { } }

Slide 30

Slide 30 text

uiScope.launch { dataFlow() .catch{ e -> println(“caught $e“} .collect { value -> println(value) } } a downstream exception must always be propagated to the collector.

Slide 31

Slide 31 text

Retrofit supports suspend function Integrated into many jetpack libraries Other reactive libraries can be easily migrated to Flow

Slide 32

Slide 32 text

Integrated with room @Dao abstract class Dao { @Query("SELECT * FROM table") abstract fun getExamples(): Flow> }

Slide 33

Slide 33 text

Integrated with live data implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version" fun LiveData.asFlow(): Flow {} fun Flow.asLiveData( context: CoroutineContext = EmptyCoroutineContext, timeout: Duration ): LiveData { }

Slide 34

Slide 34 text

● Producer ● consumer

Slide 35

Slide 35 text

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) }

Slide 36

Slide 36 text

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.

Slide 37

Slide 37 text

@Test fun testMultipleValues() = runBlocking { val producer = FakeFlowProducer() val multipleValues = producer.multipleValues().toList() assertThat(multipleValues, isEqualTo(ALL_VALUES)) }

Slide 38

Slide 38 text

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)

Slide 39

Slide 39 text

No content