Blog Infos
Author
Published
Topics
Published
Topics

To understand flows better we need to revisit suspend functions and coroutines.

A suspending function is simply a function that can be paused and resumed at a later time. How does it do that? The compiler is doing the magic here. The compiler is just taking code that looks procedural and turning it into callbacks under the hood. Continuation is the object which tracks where to pause and where to resume.

Let’s assume we have the below code which has 2 suspension points.

val a = a()
val y = foo(a).await() // suspension point #1
b()
val z = bar(a, y).await() // suspension point #2
c(z)

This is what the compiler generates internally. There are three states for this block of code:

  • initial (before any suspension point)
  • after the first suspension point
  • after the second suspension point
class <anonymous_for_state_machine> extends SuspendLambda<...> {
    // The current state of the state machine
    int label = 0
    
    // local variables of the coroutine
    A a = null
    Y y = null
    
    void resumeWith(Object result) {
        if (label == 0) goto L0
        if (label == 1) goto L1
        if (label == 2) goto L2
        else throw IllegalStateException()
        
      L0:
        // result is expected to be `null` at this invocation
        a = a()
        label = 1
        result = foo(a).await(this) // 'this' is passed as a continuation 
        if (result == COROUTINE_SUSPENDED) return // return if await had suspended execution
      L1:
        // external code has resumed this coroutine passing the result of .await() 
        y = (Y) result
        b()
        label = 2
        result = bar(a, y).await(this) // 'this' is passed as a continuation
        if (result == COROUTINE_SUSPENDED) return // return if await had suspended execution
      L2:
        // external code has resumed this coroutine passing the result of .await()
        Z z = (Z) result
        c(z)
        label = -1 // No more steps are allowed
        return
    }          
}

Continuation object tracks the state of the suspend function. It updates the label as we move from one suspension point to another. In other words, compiler generates the code which tracks the state of suspend function across different suspension points

The Coroutine context determines on which thread the coroutines will run. There are four options:

  • Dispatchers.Default – for CPU-intense work (e.g. sorting a big list)
  • Dispatchers.Main – this will depend on what you’ve added to your programs runtime dependencies (e.g. kotlinx-coroutines-android, for the UI thread in Android)
  • Dispatchers.Unconfined – runs coroutines unconfined on no specific thread
  • Dispatchers.IO – for heavy IO work (e.g. long-running database queries)

Coroutine Scope is a way to keep track of all coroutines that run in it. Every coroutine must run in a scope. Structured concurrency in Kotlin Coroutines requires developers to always launch coroutines in the context of CoroutineScope or to specify a scope explicitly.

A coroutine is typically launched using launch coroutine builder:

fun CoroutineScope.launch(
 context: CoroutineContext = EmptyCoroutineContext,
 // …
): Job

It is defined as an extension function on CoroutineScope and takes a CoroutineContext as a parameter, so it actually takes two coroutine contexts (since a scope is just a reference to a context). What does it do with them? It merges them using the plus operator, producing a set union of their elements.

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

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

fun interface FlowCollector<T> {
    suspend fun emit(value: T)
}

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

 

Job Offers

Job Offers

There are currently no vacancies.

OUR VIDEO RECOMMENDATION

Jobs

These are simply 2 methods that are marked suspend and flow and collector are just function calls they work in tandem.

If we look at the compiled code we will find that it simply uses suspend function to create a continuation object which marks how both flow and collecter work in tandem.

it uses suspend function to create compiled code which is basically sequential in nature and scope to control the lifecycle of the flow.

Sources:

A suspending function is simply a function that can be paused and resumed at a later time. How does it do that? The compiler is doing the magic here. The compiler is just taking code that looks procedural and turning it into callbacks under the hood. Continuation is the object which tracks where to pause and where to resume.

https://kt.academy/article/how-flow-works

This article was originally published on proandroiddev.com on December 16, 2022

YOU MAY BE INTERESTED IN

YOU MAY BE INTERESTED IN

blog
It’s one of the common UX across apps to provide swipe to dismiss so…
READ MORE
blog
Hi, today I come to you with a quick tip on how to update…
READ MORE
blog
Automation is a key point of Software Testing once it make possible to reproduce…
READ MORE
blog
Drag and Drop reordering in Recyclerview can be achieved with ItemTouchHelper (checkout implementation reference).…
READ MORE

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.

Menu