preface

Nice to meet you

In the last article in this series, we learned about Kotlin generics. Using generics makes our code reusable and avoids cast exceptions. For those who haven’t read the last article, we recommend reading the “Kotlin” series first. Next, we will enter the study of Kotlin coroutine. In my opinion, Kotlin coroutine is also one of the difficult knowledge points, so I will try to explain it in easy to understand language, hoping to lead you to conquer Kotlin coroutine through my article

The problem

Let’s start with a series of questions that will make it easier to learn Kotlin coroutines:

1. What is concurrency? What is parallelism?

2. What is multitasking? What is collaborative multitasking? What is preemptive multitasking?

3. What is synchronization? What is asynchrony?

4. What is non-blocking? What is blocking?

5. What is suspension?

6. What is a non-blocking suspend?

What is a coroutine?

What is a Kotlin coroutine?

What is the use of Kotlin coroutines?

1. What is concurrency? What is parallelism?

1) Concurrency means that only one instruction is being executed at the same time, but because the CPU time slice is very small, multiple instructions can be switched quickly, which makes us seem to have the effect of simultaneous execution, existing in single-core or multi-core CPU system

2) Parallelism refers to multiple instructions being executed at the same time, which exists in a multi-core CPU system

For example, if a person buys three steamed buns, he can only eat one at a time, which is concurrent. And 3 people each buy a steamed bread, so they can eat steamed bread at the same time, this is parallel. The difference between concurrency and parallelism is whether the same time is happening at the same time

2. What is multitasking? What is collaborative multitasking? What is preemptive multitasking?

1) multitasking means that the operating system can handle multiple tasks at the same time. For example, I can open AndroidStudio and netease cloud music on my laptop and listen to songs while I am lifting code

2) Cooperative multitasking is a task that gets CPU time, unless it gives up the CPU itself, it will completely occupy the CPU. Therefore, tasks need to cooperate. After using the CPU for a period of time, they give up the use, and other tasks also do the same, so as to ensure the normal operation of the system. Typically found in earlier operating systems, such as Windows 3.1

3) Preemptive multitasking is that the operating system allocates the CPU usage time of each task. After a task has been using CPU for a certain period of time, the operating system will deprive the CPU usage of the current task and put it at the end of the query queue before asking for the next task. It appears on current operating systems, such as Windows 95 and later

The difference between cooperative and preemptive multitasking: In cooperative multitasking, if a task is deadlocked, the system is also deadlocked. In preemptive multitasking, if a task is deadlocked, the system still works

3. What is synchronization? What is asynchrony?

Synchronization and asynchrony in the computer world are not the same as synchronization and asynchrony in our daily life, which is difficult for many people to understand

1) Synchronization in the field of computer is a serial processing mode in which the caller sends a call instruction and waits for the instruction to finish execution before continuing to execute it

2) Asynchronism in the field of computer is a parallel processing mode in which the caller sends a call instruction and continues to execute it without waiting for the completion of the execution of the instruction

4. What is blocking? What is non-blocking?

Blocking is simply the literal meaning of the word. In Android, blocking means blocking the main thread. Non-blocking means not blocking the main thread

5. What is suspension?

To suspend is to save the current state and wait for execution to resume. In Android, to suspend means to do the work of the main thread without affecting it.

6. What is a non-blocking suspend?

Non-blocking suspension means that the main thread is not stuck and the program is switched to another specified thread for execution

What is a coroutine?

Coroutine, English name Coroutine, derived from Simula and Modula-2, is a cooperative multi-tasking implementation, a programming idea that is not limited to a specific language. Coroutines are designed to solve concurrency problems and make collaborative multitasking easier to implement

What is a Kotlin coroutine?

Kotlin coroutines are simply a set of thread manipulation frameworks. In particular, it is a set of threaded implementation of a higher level of tool apis, similar to Java thread pools. You can understand that Kotlin has created some new concepts to help you use these apis better, but that’s all

What is the use of Kotlin coroutines?

1) Kotlin coroutines can help you gracefully handle callback hell by writing asynchronous code in a way that looks synchronous

Now that these questions are clear, let’s move on

Kotlin coroutine ecology and dependency library

Instead of including coroutines in the standard library, Kotlin provides them in the form of dependency libraries. Here is an ecological map of Kotlin coroutines:

As you can clearly see from the above figure, the Kotlin standard library and coroutine dependencies are provided. When we create a Kotlin project, the coroutine dependencies are imported by default, so we can add the following coroutine dependencies here, which can be viewed by clicking on the portal:

// Coroutine core library
implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines -- core: 1.4.3'
// This library is only used in Android projects
implementation 'org. Jetbrains. Kotlinx: kotlinx coroutines - android: 1.4.3'
Copy the code

Create your first coroutine using the GlobalScope.launch function

  • The GlobalScope.launch function creates a coroutine scope so that the code block passed to the launch function runs in the coroutine
  • The GlobalScope.launch function creates a top-level coroutine that ends when the application runs
  • The GlobalScope.launch function creates a coroutine that is a bit like a thread, in that a thread has no top-level function and will always be top-level
fun main(a) {
    GlobalScope.launch {
        println("codes run in coroutine scope")}}Copy the code

The above short code is to open a coroutine, very simple, a line of code to achieve, coroutine is no more than this 😂. There’s actually a ton of knowledge behind this code:

1. Coroutine scope

2. Extension function of coroutine scope

Coroutine context

4. Coroutine startup mode

You might be wondering, how can there be so many things involved in just one line of code? Click on the launch function to see the source code:

// launch function source code
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope. () - >Unit
): Job {
  	/ /...
}
Copy the code

The launch function is an extension of the CoroutineScope. It takes three parameters: the first parameter is CoroutineContext, which has a default value. The second parameter, CoroutineStart, which is the coroutine startup mode, has a default value. Third argument: function type argument, no default value. So when the launch function is actually called, all you need to do is pass in a Lambda expression, or you can pass in arguments to override the default

Ok, knowing that there are so many things involved in it, now let’s do it piecemeal. I’m going to talk about coroutine scopes. The rest of them are a little bit boring in this article, but we’ll do it in the next article

Coroutine scope

Going back to the beginning of the code, let’s first look at GlobalScope and see its source code:

public object GlobalScope : CoroutineScope {
    /** * Returns [EmptyCoroutineContext]. */
    override val coroutineContext: CoroutineContext
        get() = EmptyCoroutineContext
}
Copy the code

GlobalScope is a singleton class that implements CoroutineScope and overwrites the coroutineContext property

1, CoroutineScope

Then click on the CoroutineScope:

public interface CoroutineScope {
    public val coroutineContext: CoroutineContext
}
Copy the code

CoroutineScope can define a CoroutineScope, and each coroutine builder like launch and async is an extension of it.

2) it is an interface that holds a CoroutineContext (CoroutineContext), which we can make a class implement as a coroutine scope

2, GlobalScope

Now back to GlobalScope, we should be able to explain it: because GlobalScope is a singleton class that implements CoroutineScope, it has global CoroutineScope and only one object instance in the entire JVM. Because it has a lifetime throughout the JVM, we need to be wary of memory leaks when using it. The globalScope. launch call in the above code essentially calls the CoroutineScope launch extension function

3. Coroutine scope scope

So is there a question in your mind: what’s the use of having coroutine scopes? It works

Coroutines must be started in the coroutine scope, which defines some rules for parent-child coroutines. The Kotlin coroutine controls all coroutines in the scope through the coroutine scope

Coroutine scopes can be parallel or included to form a tree structure, which is the structured concurrency in Kotlin coroutine. The rules are as follows:

4. Scope segmentation

There are three kinds:

1) Top-level scope: scope of coroutines without parent coroutines

2) Coscope: when a new coroutine (child coroutine) is started, the scope of the child coroutine is the coscope by default. All uncaught exceptions thrown by the child coroutine will be passed to the parent coroutine for processing, and the parent coroutine will also be cancelled at the same time.

3) Master-slave scope: it is the same as the parent-child relationship of the synergy scope, except that the child coroutine will not be passed up to the parent if an uncaught exception occurs

5. Rules between parent-child coroutines

1) If the parent coroutine is cancelled or terminated, then all children below it are cancelled or terminated

2) The parent coroutine will enter the completed state only after the execution of the child coroutine is completed, regardless of whether the code block of the parent coroutine itself has been executed

3) The child coroutine inherits the element in the parent coroutine context. If it has a member with the same Key, it overwrites the corresponding Key, and the overwriting effect is only valid within its own scope

Ok, so do you understand the coroutine scope at this point? If you don’t, keep reading. Maybe as you learn more, your question will be solved

Use Delay function to Delay coroutine execution

  • The delay function is a non-blocking suspend function that can delay the execution of the current coroutine until a specified time and can only be called from the scope of the coroutine or from other suspend functions

  • In contrast to thread.sleep (), delay only suspends the current coroutine and does not affect the execution of other coroutines, while thread.sleep () blocks the current Thread and all coroutines under that Thread will be blocked

fun main(a) {
    GlobalScope.launch {
        println("codes run in coroutine scope")}}Copy the code

If you run the above code, you will find that the log cannot be printed. Do you have many question marks? 😂

This is because the application ends before the code in the code block can be executed. To solve this problem, we can delay the application for some time before it finishes, as follows:

fun main(a) {
    GlobalScope.launch {
        println("codes run in coroutine scope")
    }  
    Thread.sleep(1000)}// Print the result
codes run in coroutine scope
Copy the code

Above we let the main thread block execution for 1 second, so the code in the block is executed. There is a bit of a problem with this. If I make the code in the block not finish running for 1 second, I will be forced to interrupt:

fun main(a) {
    GlobalScope.launch {
        println("codes run in coroutine scope")
      	delay(1500)
        println("codes run in coroutine scope finished")
    }  
    Thread.sleep(1000)}// Print the result
codes run in coroutine scope
Copy the code

In the above code, we added a delay function to the code block and printed a line of log after it. The current coroutine will hang for 1.5 seconds and the main thread will block for only 1 second. Run the program again and the new log will not be printed because the program will end before it can run.

Is there a way to finish the coroutine after all the code has been executed? 🤔 ️

A: Yes, use the runBlocking function

Create a coroutine scope that blocks the current thread using the runBlocking function

  • The runBlocking function guarantees that the current thread will block until all code and subcoroutines in the scope of the coroutine have executed

Note: The runBlocking function is usually only used in test environments, and using it in a formal environment can cause some performance issues

fun main(a) {
    runBlocking {
       println("codes run in coroutine scope")
       delay(1500)
       println("codes run in coroutine scope finished")}}// Print the result
codes run in coroutine scope
codes run in coroutine scope finished
Copy the code

Using the runBlocking function, you can see that both logs print normally. At this point, I have a question in my mind: can I create multiple coroutines that all run in the same coroutine?

A: Yes, use the launch function

Create subcoroutines in the current coroutine scope using the launch function

As we mentioned above, the launch function is an extension of CoroutineScope, so you can call it as long as you have CoroutineScope

  • Using the launch function alone is different from the GlobalScope.launch function we just used. Globalscope. launch creates a top-level coroutine, whereas the launch function creates subcoroutines
fun main(a) {
    runBlocking {
        launch {
            println("launch1")
            delay(1000)
            println("launch1 finished")
        }

        launch {
            println("launch2")
            delay(1000)
            println("launch2 finished")}}}// Print the result
launch1
launch2
launch1 finished
launch2 finished
Copy the code

In the above code, we called launch twice, creating two subcoroutines. After running, we can see that the logs of the two subcoroutines are printed alternately, which indicates that they are running concurrently as if they were multithreading. However, the two subcoroutines actually run in the same thread, and it’s up to the programming language to decide how to schedule between multiple coroutines, who to run and who to suspend. The scheduling process requires no operating system involvement at all, which makes the concurrency efficiency of coroutines surprisingly high

Currently, the logic in the launch function is relatively simple, so as the logic becomes more and more, we may need to extract some of the code into a separate function, as follows:

fun performLogistics(a){
    // Process tons of logical code
    / /...
    The compiler will report an error because the delay function can only be called from the coroutine scope or from other pending functions
    delay(1500)
    / /...
}
Copy the code

The delay function cannot be called if it is in a single function. Is there another way?

We know that the delay function can only be called in the coroutine scope or any other pending function. Now we extract a separate function that has no coroutine scope. Can we declare it as a pending function?

A: Yes, you can declare a function as a suspend function using the suspend keyword. Suspend functions can call each other

Declare a function as a suspend function using the suspend keyword

  • The suspend keyword enables you to declare a function as a suspend function
  • The suspended function must be called from either a coroutine or another suspended function

So the above code we add a keyword to modify ok, as follows:

suspend fun performLogistics(a){
    // Process tons of logical code
    / /...
    delay(1500)
    / /...
}
Copy the code

Now the question is, if I want to call the launch function from the suspended function, can I? As follows:

suspend fun performLogistics(a){
    // Process tons of logical code
    / /...
    delay(1500)
    / /...
    // The compiler will report an error because there is no coroutine scope
    launch{
      
    }
}
Copy the code

This code fails again because there is no coroutine scope, so if I want to call this, can I do it?

A: Yes, use the coroutineScope function to solve the problem

Create a coroutineScope using the coroutineScope function

  • The coroutineScope function inherits the external coroutineScope and creates a subscope
  • The coroutineScope function is also a suspend function, so we can call it in any other suspend function
suspend fun printDot(a) = coroutineScope {
    println(".")
    delay(1000)
    launch {

    }
}
Copy the code

The above code calls the launch function without error.

Also, the coroutineScope function is similar to the runBlocking function in that it guarantees that all code and child coroutines in its scope will block the current coroutine until they have all executed. As a result, runBlocking is blocking the current thread at all times.

fun main(a) {
    runBlocking {
        coroutineScope {
            launch {
                for (i in 1.. 5) {
                    println(i)
                }
            }
        }
        println("coroutineScope finished")
    }
    println("runBlocking finished")}// Print the result
1
2
3
4
5
coroutineScope finished
runBlocking finished
Copy the code

From the printed results, we can verify the above conclusion

Use async to create a child coroutine and get the result

Launch can create a child coroutine. However, launch can only be used to execute a piece of logic, but it cannot get the result of the execution because its return value is always a Job object. So if we want to create a child coroutine and get the result of its execution, We can use async functions

  • Async functions must be in coroutine scope to be called
  • The async function creates a child coroutine and returns a Deferred object. To get the execution result in the block of async function code, simply call the await() method of the Deferred object
  • Async functions are executed immediately after being called. When an await() method is called, if the code in the block is not finished executing, the await() method blocks the current coroutine until the result of the execution in the async function can be obtained
fun main(a) {
    runBlocking {
        val start = System.currentTimeMillis()
        val result1 = async {
            delay(1000)
            5 + 5
        }.await()


        val result2 = async {
            delay(1000)
            4 + 6
        }.await()
        println("result is ${result1 + result2}")
        val end = System.currentTimeMillis()
        println("cost: ${end - start} ms.")}}// Print the result
result is 20
cost: 2017 ms.
Copy the code

The above code uses two async functions in a row to perform the task, with a delay of 1 second in the block. As mentioned above, the await() method blocks the current coroutine until the async function block is finished executing. The execution time of the whole code is 2017 ms, indicating that the two async functions here are indeed a serial relationship, and the first one can only be executed after the next one is completed. Async async async async async async async async async async asynC asynC

fun main(a) {
    runBlocking {
        val start = System.currentTimeMillis()
        val deferred1 = async {
            delay(1000)
            5 + 5
        }


        val deferred2 = async {
            delay(1000)
            4 + 6
        }
        println("result is ${deferred1.await() + deferred2.await()}")
        val end = System.currentTimeMillis()
        println("cost: ${end - start} ms.")}}// Print the result
result is 20
cost: 1020 ms.
Copy the code

Instead of immediately using await() method after each call to async function, we call await() method only when we need to use the result of async function execution. Thus async function becomes an asynchronous relationship. You can see that the print confirms this

I am a lazy person. Async functions need to call await() method every time to get results, which is tedious. Then I wonder: Are there any functions similar to async functions that do not need to call await() method every time to get results?

A: Yes, use the withContext function

Build a simplified version of async using the withContext function

  • The withContext function is a suspend function and forces us to specify a coroutine context parameter. The scheduler is essentially specifying the thread on which the coroutine will run
  • The withContext function executes immediately after it is called and ensures that all code and subcoroutines in its scope block the current coroutine until they have all been executed
  • The withContext function creates a subcoroutine and returns the result of the execution of the last line
fun main(a) {
    runBlocking {
        val result = withContext(Dispatchers.Default) {
            5 + 5
        }
        println(result)
    }
}

// Print the result
10
Copy the code

Use suspendCoroutine to simplify callbacks

In our daily work, we usually use an asynchronous callback mechanism to retrieve network response data. If you noticed, this callback mechanism basically relies on anonymous inner classes, such as the following code:

sendHttpRequest(object : OnHttpCallBackListener{
    override fun onSuccess(response: String){}override fun onError(exception: Exception){}})Copy the code

The number of anonymous inner classes that need to be written to implement network requests in as many places as possible can be particularly tedious. Before we learned about the Kotin coroutine, there was probably no easier way to write callbacks, but now we can use the suspendCoroutine function in the Kotlin coroutine to simplify writing callbacks:

  • The suspendCoroutine function, which must be called from the coroutine scope or suspension function, takes a Lambda expression, suspends the current coroutine immediately, and then executes the code in the Lambda expression in a normal thread
  • The suspendCoroutine function’s Lambda expression argument list passes ina Contination argument, and calling its resume() or resumeWithException() method can resume execution of the coroutine
// Define successful and failed interfaces
interface OnHttpCallBackListener{
    fun onSuccess(response: String)
    fun onError(exception: Exception)
}

// Simulate sending a network request
fun sendHttpRequest(url: String, httpCallBack: OnHttpCallBackListener){}// Encapsulate the sent network request callback with the suspendCoroutine function
suspend fun request(url: String): String{
    return suspendCoroutine { continuation ->
        sendHttpRequest(url,object : OnHttpCallBackListener{
            override fun onSuccess(response: String) {
                continuation.resume(response)
            }

            override fun onError(exception: Exception) {
                continuation.resumeWithException(exception)
            }

        })

    }
}

// Specify the usage
suspend fun getBaiduResponse(a){
    try {
        val request = request("https://www.baidu.com/")}catch (e: Exception) {
        // Handle exceptions}}Copy the code

In the above code:

1. We used the suspendCoroutine function just described inside the request function so that the current coroutine is immediately suspended and the code in the Lambda expression is executed in a normal thread. We then invoke the sendHttpRequest() method in the Lambda expression to initiate the network request and listen for the result of the request through traditional callbacks

2. If the request succeeds, the resume() method of the Continuation is called to resume the suspended coroutine and the server responds with data that becomes the return value of the suspendCoroutine function

3. If the request fails, call the Continuation’s resumeWithException() method to restore the suspended coroutine, passing in the specific exception cause

4. Finally, I used it in getBaiduResponse(). Do you feel the code here is much clearer? Since getBaiduResponse() is a suspended function, when getBaiduResponse() calls request(), the current coroutine will be suspended immediately, and then wait for the network request to succeed or fail, the current coroutine can resume operation

5. If the request succeeds, we get the response data of the asynchronous network request. If the request fails, it goes directly to the CATCH statement

The getBaiduResponse() function is declared as a pending function, so it can only be called in the coroutine scope or other pending functions. Is it too limited to use?

A: This is true because suspendCoroutine functions are designed to be used with coroutines, so we need to solve this problem through proper project architecture

After the above steps, we used the suspendCoroutine function to write asynchronous code in a seemingly synchronous way. In fact, the suspendCoroutine function can be used to simplify writing almost any callback. For example, if we use Retrofit in a real project, we can use the suspendCoroutine function to simplify the callbacks

Now that you’ve got some idea of coroutines, let’s go a little deeper

Suspend operations in Kotlin

Suspend is one of the dark arts of Kotlin coroutines. We briefly described how to define a suspend function using suspend. Now let’s take a closer look at Kotlin’s suspend operations

1. The nature of hanging

The following code:

class MainActivity : AppCompatActivity() {

    private val TAG: String = "MainActivity"
  
    override fun onCreate(savedInstanceState: Bundle?). {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        Log.d(TAG, "start... ");
        GlobalScope.launch(Dispatchers.Main) {
            mockTimeConsume()
            Log.d(TAG, "I'm executing my pending function.");
        }
        Log.d(TAG, "I executed tons of code on the main thread.");
    }


    // Simulate a pending function time-consuming task
    suspend fun mockTimeConsume(a) = withContext(Dispatchers.IO){
        Log.d(TAG, "In the intense execution of time-consuming tasks... " +  + Thread.currentThread().name);
        Thread.sleep(3000)}}// Print the following resultstart... I was executing tons of code on the main thread in a tight execution time task... DefaultDispatcher-worker-2I am executing the suspend function after it has finished executingCopy the code

The code steps above:

Create a top-level coroutine in the main thread and specify that the coroutine runs in the main thread

Execute mockTimeConsume in coroutine and print a Log

Now let’s analyze it in terms of threads and coroutines:

As I mentioned earlier in my answer to the question, to suspend is to switch to another specified thread for execution

thread

Thread: So when you execute the mockTimeConsume() in the coroutine and the coroutine is suspended due to a suspend function, the main thread will jump out of the coroutine and continue if there is more code below, or refresh its interface if there is none

coroutines

Coroutines: When you execute mockTimeConsume() in the coroutine, the current coroutine will be suspended because a suspend function is encountered. Note that the entire coroutine is suspended, meaning that nothing below mockTimeConsume() will be executed. Wait for my code to finish executing and then execute the contents of the suspended function in the specified thread. Who appointed it? Is specified by the current suspension function, such as in our case, the IO thread specified by the dispatchers. IO passed in by the withContext inside the function

Dispatchers, which can restrict coroutine execution to a specific thread, dispatch it to a thread pool, or let it run unrestricted

There are three kinds of Dispatchers in common use:

  • Dispatchers.Main: the Main thread in Android
  • Dispatchers.IO: Optimized for disk and network IO for IO intensive tasks such as reading and writing files, manipulating databases, and network requests
  • Dispatchers.Default: Suitable for CPU intensive tasks such as computing

When the suspension function is finished, the most exciting thing that the coroutine does for us is to restore the current coroutine, cutting the thread from another thread back to the current thread. Then the log. d(TAG, “I’m executing the suspended function “) line in the coroutine is executed, and the whole process ends

Through the above analysis of both threads and coroutines, we can draw some conclusions:

Suspend functions decorated by suspend have two more operations than normal functions:

1) Suspend: Suspend the execution of the current coroutine and save all local variables

2) Resume: Execution of the coroutine continues from where it was suspended

2. When a coroutine executes to a suspend function labeled suspend, it is suspended, and by suspend, it switches threads

3. After a coroutine is suspended, it needs to be recovered, which is done by the coroutine framework

Conclusion 3: If you do not call the suspended function in the coroutine, the recovery function cannot be implemented, so it answers the question: why must the suspended function be called in the coroutine or another suspended function

Consider this logic: a suspended function is either called from a coroutine or another suspended function, so it is always called, directly or indirectly, from a coroutine

Therefore, the suspend function is required to be called only in the coroutine or in another suspend function, so that the coroutine can be cut back after the suspend function switches threads

2. How was it suspended?

Is there another question on your mind at this point: how did the coroutine get suspended? If the suspension function above was written like this:

suspend fun mockTimeConsume(a){
   Log.d(TAG, "In the midst of intense execution of time-consuming tasks..." + Thread.currentThread().name);
   Thread.sleep(3000)}Copy the code

After running it, you will find that the printed thread is the main thread, so why didn’t you switch the thread? Because it doesn’t know where to cut, we need to tell it, and we wrote it like this before:

suspend fun mockTimeConsume(a) = withContext(Dispatchers.IO){
   Log.d(TAG, "In the intense execution of time-consuming tasks... " +  + Thread.currentThread().name);
   Thread.sleep(3000)}Copy the code

We can see that the difference is actually the withContext function.

It takes a Dispatcher parameter, and depending on the Dispatcher parameter, your coroutine is suspended and then cut to another thread

Therefore, suspend functions defined by suspend are not really suspend functions. A real suspend function calls the suspended functions of the Kotlin coroutine framework internally

Therefore, we want to write a suspend function of our own, not just by using the suspend keyword, but also by calling the Kotlin coroutine framework’s own suspend function directly or indirectly

3. Meaning of suspend

From the above analysis, we know that a function decorated with the suspend keyword may not be a true suspend function. What does it do?

Serves as a reminder to the caller that I am a time-consuming function that needs to be called in a pending function or coroutine

Why does the suspend keyword not actually operate on suspend, but Kotlin provides it?

Because it’s not meant to handle suspension.

The suspended operation — that is, thread cutting — relies on the actual code inside the suspended function, not on this keyword

So this keyword, it’s just a reminder.

suspend fun mockTimeConsume(a){
   Log.d(TAG, "In the midst of intense execution of time-consuming tasks..." + Thread.currentThread().name);
   Thread.sleep(3000)}Copy the code

This code does not make sense, and it affects the performance of your program. AndroidStudio will also tell you that the suspend modifier is redundant.

4, how to customize a suspend function?

Step 1: Analyze when to use suspend functions?

If you have a function that is time-consuming and involves multi-threaded operations such as network requests, I/O operations, CPU computation work, etc., that need to wait, write it as a suspend function as a matter of principle

Step 2: Decorate your function with the suspend keyword and call the suspend function provided by the Kotin coroutine framework inside the function as follows:

suspend fun mockTimeConsume(a) = withContext(Dispatchers.IO){
   Log.d(TAG, "In the intense execution of time-consuming tasks... " +  + Thread.currentThread().name);
   Thread.sleep(3000)}Copy the code

Xiii. Conclusion

This article talks about:

1. Understanding of some basic concepts involved in Kotlin coroutines

2. Basic use of Kotlin coroutines, and some coroutine scope builders

3, Kotlin coroutine on the suspension of a detailed introduction

The content of coroutines is really very much, I will write two articles about coroutines in the future, hoping to bring help to you 🤝

Thank you for reading this article

References and recommendations

[First line of code Android Version 3]

Throw line – The hang of Kotlin coroutine is amazing and difficult to understand? I took the skin off him today

Full text here, the original is not easy, welcome to like, collect, comment and forward, your recognition is the power of my creation

Please follow my public account and search sweetying on wechat. Updates will be received as soon as possible