First, we’ll remove all the auxiliary code to measure the execution time of the code and output the log. We don’t need this anymore. Now our coroutine will look like the example below. Let me remind you that the receiveData() function is suspended and will return the result, which we’ll write to a variable: val data. Then we’ll use this data as a toast message.

The code is quite simple, which is good because it will allow you to better understand the topic, especially if you are taking your first steps in this direction.

Now we’ll look on decompiled Kotlin Bytecode. I specifically removed all the code and left only the functions that are called in it.

The invoke function will be called first. It’ll be launched by the CoroutineStart class inside the launch function.

The invoke function takes the suspend function and an instance of the Continuation class as input parameters. We’ll talk about this class in more detail, but for now, here is what you need to know: there are two objects that will be used when running code inside the coroutine.

We need to go back and look at the decompiled Kotlin Bytecode for the invoke function. There, we will find these two objects. If we compare, we get:

  • Object var1 is block: suspend() -> T
  • Object var2 is completion: Continuation<T>

As we remember, a total of three functions were called in the coroutine, and we looked at one of them. In turn, the remaining two functions will be called inside the invoke function: create and invokeSuspend.

A closer look shows that the create function forms an object of the Continuation class.

Moreover, this function is contained in the base class Continuation. And not only this, but, as we’ll see, the invokeSuspend function, which we’ll look at a bit later.

And finally, the third function that will be called when the Continuation class object is created is invokeSuspend. It contains all the code that the coroutine should execute. As we already understood, this function will be called by an object of the Continuation class when the coroutine starts.

The main purpose of the Continuation class is to ensure that the user interface operation (in our case, showing toast) is performed only after receiving the result of the asynchronous operation of our receiveData function. To do this, the code inside the invokeSuspend function will be split into parts, and a label variable will be added to switch between pieces of code.

There are two objects inside: Object var3 and Object var10000. In Object var10000 will be put the result of the suspended function work, and Object var3 contains a special constant: COROUTINE_SUSPENDED.

The point where the code splits into two parts is the suspend function. It and all the code before it will go into the first part. And all the code after it is in the second. Now, when calling the invokeSuspend function, the label variable will determine which part of the code will be executed. If label is 0, then the first part of the code (our receiveData function) will be executed, and if label is 1, then the second part of the code will be executed, where the result of the work will be written to the variable var10000 and then used for display in toast.

As we figured out, the invokeSuspend function will be called for the first time at the start of the coroutine and will execute the first part of the code in case = 0, which will launch the suspend function, and the invokeSuspend function will complete (return).

Before calling the suspend function, a new value of 1 will be written to the label. This is necessary so that when the result is received, the second block of code from case 1 will start working.

But a logical question arises: how will the function be called if it has completed its work?

Note that the receiveData function accepts this as a parameter. That is, the object of the Continuation class itself is passed to the suspend function.

When the suspend function finishes its work, it will take the Continuation object that was passed to it and call its invokeSuspend function. Since the value of label was previously replaced by 1, when invokeSuspend starts running again, the second part of the code from case 1 will be executed.

Actually, only two results can come from the suspend function.

  • The first one is the return of the COROUTINE_SUSPENDED constant that was mentioned before. It means that the suspend function has not completed its work. In this case, the invokeSuspend function will be completed again (return). This will continue until the second possible option comes from the suspend function.
  • The second one is if some value other than the COROUTINE_SUSPENDED constant is returned. This will be the result of the suspend function. The code will continue.

In case 1, the result of the suspend function will be written to the var10000 variable, and then the code will continue its execution. This variable, as we see in the code below, is used to display the toast, after which the function will complete its work.

Source link