We already know what cold & hot streams are (and if you don’t, check the previous article, link below). Now, let’s discover our first Kotlin Stream implementation: Flows.

Again, you can see some examples by checking the GitHub Repository. There are very small and simple cases that will help you to understand how to use Flows and some other stuff.

Remember that this article belongs to a series:

Let’s go with part two: Flows!

Flows

What is a Flow?

A Flow is used to represent a sequential emission of values that, at some point, ends (because it naturally ends or something bad happens).

All the operations in a Flow are executed sequentially inside the same block of code (and, therefore, the same Coroutine Scope).

An important characteristic of Flow is that it only starts emitting values when someone asks for them. Before someone subscribes (or performs a collection), the Flow doesn’t run its code.

So, a Cold Stream, right?

Well, if we look at the documentation:

The Flow interface does not carry information whether a flow truly is a cold stream_ that can be collected repeatedly and triggers execution of the same code every time it is collected, or if it is a hot stream that emits different values from the same running source on each collection. However, conventionally flows represent cold streams._

They’re not very definitive. We can assume that yes, they’re cold streams, but we’ll revisit this statement later.

Behind the scenes, a Flow is just an interface that exposes a method to collect the emitted values:

public interface Flow<out T> {
	    public suspend fun collect(collector: FlowCollector<T>)
	}

Upstream and Downstream Flows

The upstream and downstream concepts help us defining “which flow I will operate” and “which flow I will produce”.

It depends on which operator we consider. Both of them are a powerful concept, because all the operators and some flow properties are strongly related to these flows.

Flow Operators

There are two types of operators: intermediates and terminals.

Intermediate operators

They are functions that are applied to the upstream flow and return a downstream flow, where we can apply other operators.

These operators don’t execute code against the flow, instead, they use the flow that they receive to apply some operations and return some data (in other words, return their version of the received flow).

Some operators don’t return any data, instead they just add some feature to the flow. We’re not going to talk about them, but just know that they exist. An example can be the cancellable one.

The intermediate operators are not suspendable functions, but they can work with suspendable functions inside. We can say that they are just used to create a chain of operations.

Some of them can be: mapfiltertaketakeWhilecatch, and a lot more. I will not explain operators here because that’s not the point of the article, but you can find a lot of documentation out there.

Terminal operators

These operators are suspendable functions and their general purpose is to collect the values received from the upstream flow.

Also, they work as the trigger of a flow execution: if there is not a terminal operator applied to the flow, the flow doesn’t start its emission.

As the name mentions, they are terminal because you cannot apply another operator (of any type) if any of them are applied.

Some of them can be: collectsingletoList, and more. Again, I will not explain operators, you can check the docs!

#kotlin #stream #android #flow #channel

Going deep on Flows & Channels — Part 2: Flows
1.45 GEEK