Home > Software design >  Kotlin Flow the best practice
Kotlin Flow the best practice

Time:11-01

what is the best practice regarding to create the new flow? For example: I have some code

    viewModelScope.launch(dispatchers.io) {
      method1().collect {
           method2().collect{} 
       }
    
}
fun method1(): Flow
fun method2(): Flow

From my point of view it is not correct to start the new flow, from collect operation. But I cannot substantiate this. Or i'm wrong and it allowable operation

CodePudding user response:

To clarify: "Creating" a Flow is not the same as "executing/starting" a flow.

You create/build a Flow by utilizing a Flow Builder. Since your two methods return Flows, this is the place where you build them.

Some examples of flow builders are:

  • flowOf(): to create flow from a given set of items.

  • asFlow(): extension function to convert collection to flow.

  • flow{}: custom flow builder block to implement any custom business logic to create flows by emitting items with emit().

For simple 'static' flow builders like flowOf(1, 2, 3), you can define Flow as properties to reduce overhead, whereas for more complex builder functions with parameters, i.e. when using flow{}, methods are the way to go, like in your example.

The Flow is a cold stream, which means, your builder code won't get executed before it is collected.

collect{} is the correct way of "starting" a Flow's execution. You literally are collecting it's items one by one, executing it's builder code.

Your example has nested collect{} calls, which is fine in some cases, but is not best practice in many cases. I recommend reading Vasya Drobushkov's blog post Nesting vs. Chaining to understand, when to use which approach. In short, chaining means better readability, error handling and testing. The preferred way is to make use of Flow methods combine() and zip() to chain your operations on multiple flows and using flatMap{} operations to apply nesting when needed, with a single collect in the end.

Here's an example:

CoroutineScope(Dispatchers.Main).launch {
    flow1.zip(flow2) { intValue, stringValue ->
        "$intValue$stringValue"
    }.collect {
        Log.d(TAG, it)
    }
}

val flow1: Flow = flowOf(1, 2, 3)
val flow2: Flow = flowOf("A", "B", "C")

Result:

1A
2B
3C
  • Related