Kotlin Flow Read This Article to Get You Started

Kotlin Flow to Get You Started Solution



Kotlin Flow see this article to get you started~

foreword
Kotlin Flow.In the last three articles, the titles are all *****, this article is enough, and this article about Flow, I admit it , I can only say that this article will get you started~, because I found that There are too many things involved in Flow, just like RxJava , let alone two or five, it can't be finished.

Kotlin Flow.Why you need Flow


Kotlin Flow.First, let's review how we use the suspend function in Kotlin. In the main method, we call the suspend function to return a set of data. The code is as follows:
suspend fun loadData ( ): List {
delay( 1000)
return listOf ( 1, 2, 3)
}
fun main( ) {
runBlocking {
loadData ( ). forEach { value -> println (value) }
}
}

Kotlin Flow.Run the main function and output 1 2 3 after the result is 1s
Then let's think about it, if the data set in loadData is not returned together, for example, 1 is obtained from the network, 2 is obtained, and finally 3 is obtained, then if we are still returning the last result (actually not Knowing) and returning data together will cause waste of resources and poor user experience, so how do we solve this problem?
The return type of the above suspend function is a List type, so it must only return data at one time. At this time, Flow comes out~
Basic use of Flow
builder
We rewrite the loadData method, change the return type to Flow, and construct a flow. Every second in the flow, a data is sent to simulate the delayed acquisition of values. The code is as follows:

fun loadData ( ) = flow {
for (i in 1..3) {
delay(1000)
emit(i)
}
}
fun main() {
runBlocking {
loadData1().collect {
println(it)
}
}
}

Kotlin Flow.The result of the operation is that every 1 second, a number is printed, the emit method is used to emit values, and the collect method is to collect values. It should be noted here that we can see that in the main method coroutine , we can directly call loadData method, this is because the code in the flow building block is a suspend function. In this way, we can gradually load the data without waiting for all the data to return.
Next, we call the loadData method multiple times in the main method without calling collect to see what happens. The modified code is as follows:
fun loadData1() = flow {
println ("Enter method to load data")
for ( i in 1.. 3) {
delay( 1000)
emit( i )
}
}
fun main( ) {
runBlocking {
println ("First time ready to call method to load data")
var data = loadData1()
println ("Second time to call method to load data")
var data2 = loadData1()
data2.collect {
println (it)
}
}
}

Then we run the main method and print the result as follows:

The method to load data is called for the first time
Second time prepare to call method to load data
Enter the method to load data
1
2
3
Process finished with exit code 0

We will find that if we do not call the collect method of the flow, it will not actually enter the code block of the flow, that is to say, the code in the flow will not run until it is called by collect, otherwise it will return immediately.
Cancellation of Flow

If we need to cancel the execution of the code block in Flow regularly, we only need to use the withTimeoutOrNull function to add the timeout time. For example, in the above method, we return 123 within three seconds, and we limit it to be executed within 2500 milliseconds.
fun main( ) {
runBlocking {
withTimeoutOrNull ( 2500){
loadData1( ).collect {
println (it)
}
}
}
}

When we run the main method, only two numbers 1 2 are printed
1
2
Process finished with exit code 0

Operators for Flow
A set-like function is Api , and there are many operators in Flow. Here are a few examples:
map
Using map we can map the final result to other types, the code is as follows:
fun changeData ( value: Int): String {
return "The result of printing is: ${value}"
}
fun main( ) {
runBlocking {
loadData1( ).map {
changeData (it)
}.collect {
println (it)
}
}
}

We use the map operator to map the result to the form of a string, and run main to print the result as follows:
The result printed is: 1
The result printed is: 2
The result printed is: 3
Process finished with exit code 0

filter operator
With filter we can add filter conditions to the result set, as shown below, we only print out values greater than 1
runBlocking {
loadData1( ).filter {
it > 1
}.collect {
println (it)
}
}

So the print result is as follows:
2
3
Process finished with exit code 0

All operators can be used together, not only individually.
The collect we called above is the terminal operator. In addition to collect, there are toList , reduce, fold and other operators in Flow.
toList operator means to convert to a list collection, and reduce and fold can convert the final value to a single value.
fun main( ) {
runBlocking {
var data = loadData1( ).reduce { a, b ->
a + b
}
println (data)
}
}

As in the above code, we finally sum each result of Flow, and the print result is as follows:
6
Process finished with exit code 0

flowOn
Flow's code block is executed in the context of execution time, for example, we cannot run the code in the Flow code by specifying a thread in the flow, as follows:
fun loadData1() = flow {
withContext ( Dispatchers.Default ){
for ( i in 1.. 3) {
delay( 1000)
emit( i )
}
}
}
fun main( ) {
runBlocking {
loadData1( ).collect { value -> println ("Collected $value") }
}
}

In this way, an exception will be thrown
Exception in thread "main" java.lang .IllegalStateException : Module with the Main dispatcher had failed to initialize. For tests Dispatchers.setMain from kotlinx -coroutines-test module can be used
at kotlinx.coroutines.internal.MissingMainCoroutineDispatcher.missing (MainDispatchers.kt:113)


So how do we specify the context in the Flow code block, we need to use the flowOn operator, we specify the code in the Flow code block in the IO thread, the code is as follows:
fun loadData1() = flow {
for ( i in 1.. 3) {
delay( 1000)
emit( i )
}
} .flowOn (Dispatchers.IO)

This way we put things in the Flow block of code into the IO thread.

buffer operator
We have seen in Kotlin coroutines that it is enough Time-consuming operation, let's see how long it takes to process, we interval two seconds before printing, and record the start and finish time, the code is as follows:
var startTime : Long = 0
var endTime: Long = 0
fun loadData1() = flow {
startTime = System.currentTimeMillis() / 1000
for (i in 1..3) {
delay(1000)
emit(i)
}
}
fun main() {
runBlocking {
loadData1().collect { value ->
delay(2000)
println ("$value")
}
endTime = System.currentTimeMillis () / 1000
println ("Processing time: ${ endTime - startTime }s")
}
}

Running the main method yields the following results:
1
2
3
Processing time: 9s
Process finished with exit code 0

Kotlin Flow.We can see that it took a total of 9 seconds to process the three data.
The buffer operator can make the code of emission and collection run concurrently, thereby improving efficiency. We add the buffer code as follows:
fun main( ) {
runBlocking {
loadData1( ).buffer ().collect { value ->
delay( 2000)
println ("$value")
}
endTime = System.currentTimeMillis () / 1000
println ("Processing time: ${ endTime - startTime }s")
}
}

Run the main method again and the result looks like this:
1
2
3
Processing time: 8s
Process finished with exit code 0

From this, it can be seen that the time is less than 1s (/1000 is an approximate value). Don't underestimate this small 1 second. It is still very important to run on a mobile phone~
zip operator
zip operator, you can merge two flows, the code is as follows:
fun loadData1() = flow {
for ( i in 1.. 3) {
delay(1000)
emit("我是j:${i}")
}
}
fun loadData2() = flow {
for (i in 1..3) {
delay(1000)
emit("我是i:${i}")
}
}
fun main() {
runBlocking {
loadData2().zip(loadData1()){
a,b -> "$a,$b"
}.collect {
println (it)
}
}
}

The result of running is as follows:
I am i:1, I am j:1
I am i:2, I am j:2
I am i:3, I am j:3
Process finished with exit code 0

Related Articles

Explore More Special Offers

  1. Short Message Service(SMS) & Mail Service

    50,000 email package starts as low as USD 1.99, 120 short messages start at only USD 1.00