1. An overview of the
The suspension function is the most important feature of the Kotlin coroutine, on which all other concepts are based. So we need to know more about how it works.
Suspending a coroutine means stopping it in the middle. This is similar to playing a game, when we want to pause the game, we can save the game first, and when we want to resume the game, we can resume the game from the save. When a coroutine is suspended, it returns a Continuation. Just like a save in a game, a coroutine can be recovered from where it was suspended using a Continuation.
Note that this is very different from threads, which cannot be saved and can only be blocked. Coroutines are much more powerful, and when suspended, they do not consume any of the thread’s resources. Coroutines do not necessarily need to be recovered on the thread that started them; they can be switched to a different thread.
2. Restore
Let’s see it in action. We use a coroutine builder, such as runBlocking or launch, to launch coroutines. The suspend function is a function that suspends the coroutine. This means that they must be called on the coroutine (or another pending function).
This is a simple program that will print “Before” and “After”. What if we call the suspendCoroutine function provided by the coroutine library in the middle?
If you execute the code above, you will find that “After” is not printed and main does not exit. The coroutine pauses after “Before”. Our program stopped and never came back. So how do we do that? Can we use a Continuation to restore the program?
Take a look at the suspendCoroutine call and note that it ends with a lambda expression ({}). The function passed as an argument will be called before the pause. This function takes a Continuation as an argument.
Looking at the print log, we see that “After” is still not printed. We can resume the coroutine with a Continuation.
Notice that “After” in the above example is printed because we call the resume function in suspendCoroutine.
Since Kotlin 1.3, the definition of continuations has changed. The resumeWith function replaces the resume and resumeWithException functions. The resume and resumeWithException we used are extension functions in the standard library.
We can also use it to start a different thread that will sleep for a while before resuming:
We can also extract the code that starts the thread into a function.
This mechanism works, but we can also use thread pools instead of threads.
Pausing for a while seems like a useful feature. Let’s extract it into a function. We’re going to call it delay.
This is exactly how the Kotlin coroutine delay method used to be implemented. The current implementation is more complex, mainly to support testing, but the essential idea is the same.
3. Restore with a value
You may want to notice why we passed Unit to the resume function and why we made Unit the suspendCoroutine function generic type. Unit is the return type of the suspendCoroutine function and the generic type of continuations.
When we call suspendCoroutine, we can specify the return type in the Continuation. The resume function needs to call the same type.
Hang makes sense for coroutines. When we do time-consuming operations, we need to be suspended. For example, when we need to get a network response from the API, if there is no coroutine, the thread will need to wait. Because threads are expensive, this would be a huge waste. Especially if it’s an important thread, like the main thread on Android. With coroutines, it just suspends the coroutine and the thread can go do something else. Once the data arrives, the thread will resume from the coroutine suspension point.
For example, we simulate a network request for user information:
It is not convenient to call suspendCoroutine directly from the main method. We can extract it as a method.
Currently, many popular libraries such as Retrofit or Room already support the suspend function. This is why you rarely need to use callback functions in your suspend function. There is a similar to suspendCoroutine suspendCancellableCoroutine, I recommend you to use the latter, the latter supports the function.
You might be wondering what would happen if the API returned an exception instead of giving us the data. What happens if the service crashes or responds incorrectly? . In this case, instead of returning data, we should throw an exception from where the coroutine hangs
4. Abnormal recovery
Each function we call may return a value or throw an exception. The same is true for suspendCoroutine. We can call resumeWithException to return from the exception. You can catch exceptions with a try catch.
Suspend a coroutine, not a function
One thing to emphasize is that we’re just suspending a coroutine, not a function. Imagine that we store the Continuation in a variable and try to restore it after the function call.
It doesn’t make any sense. Resume is never called.
Welcome to the official wechat account of bytecode.