Detailed explanation and use of coroutine suspend in Android kotlin combat

foreword

        Kotlin is a language that only provides the most basic low-level API in the standard library so that various other libraries can take advantage of coroutines. Unlike many other languages ​​with similar functionality, async with  await is not a keyword in Kotlin, nor is it even part of the standard library. Furthermore, Kotlin's  concept of suspending functions  provides a safer and less error-prone abstraction for asynchronous operations than futures and promises.

        kotlinx.coroutines is a feature-rich coroutine library developed by JetBrains. It contains many of the advanced coroutine-enabled primitives covered in this guide, including  launch, ,  async and more.

 For other kotlin usage, you can check the following:

Android Kotlin's practical high-level use of generic extension coroutine lazy loading detailed explanation - snail, Z's blog - CSDN blog

Summary of Android kotlin's problems in the actual combat process and detailed explanation of development skills

Detailed explanation of Kotlin grammar and practical tutorials, distinguishing JAVA and how to close the pit_kotlin mapof can be nested to_Snail, Z's Blog-CSDN Blog

What is coroutine suspend

        The literal meaning of suspend is to suspend and suspend. In kotlin, when the code executes to the suspend function, it will "suspend", and this "suspend" is non-blocking, it will not block your current thread, the location of the suspension: temporarily cut off, and then cut off later Come back, it is different from Java's Thread.sleep(), one is blocking and the other is waiting, similar to wait.

The basic principle of blocking is to occupy the CPU all the time. If sleep is not given up, what is suspended is to give up its own cup time and wait for reallocation. This is easy to understand. The most intuitive understanding is an asynchronous operation

Coroutine method call

If the current method is modified by suspend, if it is not coroutines called by runblock or library, then all functions in the call chain must be modified by suspend

1. Modification method

suspend fun main() {
    delay(1000)
    println("hello")
}

And because it is difficult to obtain the main method in our business, we cannot solve the call of the coroutine at the source through the main method

2. With the help of tools

2.1 Synchronization method runBlocking

runBlocking is a blocking call. If the coroutine is processed through it, it will only be executed after the method body of runBlocking is executed, and it will occupy the CPU until it is finished running.

 fun main() {
    runBlocking {
        delay(1000)
        println("hello---block")
    }
    println("hello")
}

 2.2 Asynchronous librarykotlinx.coroutines 

The call of the asynchronous library is to suspend the thread code first, continue to execute, and then return to the execution of the coroutine function after the execution of the following is completed.

fun main() {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        println("suspend start!") // 在延迟后打印输出
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("suspend end!") // 在延迟后打印输出
    }

    println("sleep start,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
    println("sleep end,") // 协程已在等待时主线程还在继续


}

 

Through the above log analysis, the coroutine will not be executed even if it is suspended. The following code is executed first, and the sleep function is encountered, which forms a blockage. The coroutine gets the CPU time again, and the coroutine code starts to execute quickly. After execution, sleep blocks and continues to execute.

coroutines library use

kotlinx.coroutines是填补了kotlin在Java中的缺失,也提供了异步处理协程的问题。

Dependent library:

   implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.1"

use

1. Asynchronously suspend the coroutine

    GlobalScope.launch { // 在后台启动一个新的协程并继续
       
        //协程代码调用处
       
    }

2. Delay

    GlobalScope.launch { // 在后台启动一个新的协程并继续

        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)

    }

Call delay directly to set the time that needs to be blocked

3. Job object

launch will return an object Job, which is the current task. If you accept this Job, the code block will not be executed and you need to call it manually

fun main() {
    val job = GlobalScope.launch { // 启动一个新协程并保持对这个作业的引用
        delay(1000L)
        println("World!")
    }

    println("Hello,")
    runBlocking {
        job.join() // 等待直到子协程执行结束
    }

    
}

The join of the job can only be called in the blocking method, or the body of the suspend method, not in the

GlobalScope.launch {
    job.join()
}

Called, otherwise the current code block will not be executed. Because launch has returned a job, the current job has not been executed, so the changed job has been in an unexecuted state.

Can also be canceled: job.cancel()

 Coroutine scope: launch{}

Comparison between GlobalScope and CoroutineScope

The stereotyped essay shows that the former is faster than the latter. Through source code analysis, GlobalScope inherits CoroutineScope, is a package of CoroutineScope, and provides a static function.

structured concurrency

        The actual use of coroutines leaves some room for improvement. When we use  GlobalScope.launch , we create a top-level coroutine. Although it's lightweight, it still consumes some memory resources while it's running. There is a better solution. We can use structured concurrency in our code. Instead of launching a coroutine in a GlobalScope like we usually do with threads (threads are always global), we can start the coroutine in the specified scope where the operation is performed.

fun main() = runBlocking { // this: CoroutineScope
    launch { // 在 runBlocking 作用域中启动一个新协程
        delay(1000L)
        println("World!")
    }
    println("Hello,")

 
}

scope builder

        In addition to coroutine scopes provided by different builders, it is also possible to declare your own scopes using the coroutineScope builder. It creates a coroutine scope and doesn't end until all started child coroutines have finished executing.

        runBlocking and coroutineScope might look similar in that they both wait for their coroutine body and all child coroutines to finish. The main difference is that the runBlocking method blocks the current thread to wait, while the coroutineScope just hangs, freeing the underlying thread for other uses. Because of this difference, runBlocking is a regular function and coroutineScope is a suspending function

fun main() = runBlocking { // this: CoroutineScope
    launch {
        delay(200L)
        println("Task from runBlocking 1")
    }

    coroutineScope { // 创建一个协程作用域
        launch {
            delay(500L)
            println("Task from nested launch 2")
        }

        delay(100L)
        println("Task from coroutine scope 3") // 这一行会在内嵌 launch 之前输出
    }

    println("Coroutine scope is over 4") // 这一行在内嵌 launch 执行完毕后才输出
}

 Hangs: GlobalScope.async

GlobalScope's async suspends asynchronously, supports return values, and cannot be used directly. It needs to be executed in the body of runBlocking.

fun main()= runBlocking {
    println("current thread 1")
    //launch启动后台调度线程,并且不堵塞当前线程
    val deferred = GlobalScope.async {
        println("async thread 2=")
        delay(1000)
        println("async end 3")
        //需要通过标签的方式返回
        return@async "123"
    }
    println("current thread end 4")
    val result = deferred.await()
    println("result 5= $result")
    //当前线程休眠以便调度线程有机会执行
    Thread.sleep(3000)
}

Coroutine context and scheduler

        The coroutine context contains a coroutine dispatcher (see CoroutineDispatcher) which determines on which thread or threads the associated coroutine is executed. The coroutine scheduler can limit coroutine execution to a specific thread, dispatch it to a thread pool, or let it run unrestricted.

launch { // 运行在父协程的上下文中,即 runBlocking 主协程
    println("main runBlocking      : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Unconfined) { // 不受限的——将工作在主线程中
    println("Unconfined            : I'm working in thread ${Thread.currentThread().name}")
}
launch(Dispatchers.Default) { // 将会获取默认调度器
    println("Default               : I'm working in thread ${Thread.currentThread().name}")
}
launch(newSingleThreadContext("MyOwnThread")) { // 将使它获得一个新的线程
    println("newSingleThreadContext: I'm working in thread ${Thread.currentThread().name}")
}

The scheduler types are as follows:

public actual object Dispatchers {

    @JvmStatic
    public actual val Default: CoroutineDispatcher = DefaultScheduler


    @JvmStatic
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher


    @JvmStatic
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined

    @JvmStatic
    public val IO: CoroutineDispatcher = DefaultIoScheduler

    @DelicateCoroutinesApi
    public fun shutdown() {
        DefaultExecutor.shutdown()
        // Also shuts down Dispatchers.IO
        DefaultScheduler.shutdown()
    }
}

Debug coroutines and threads

        Coroutines can be suspended on one thread and resumed on other threads. Without special tools, it is difficult even for a single-threaded scheduler to figure out when and where coroutines are doing what.

start method   

runBlocking Create a new coroutine and run it on the current thread, so it will block the current thread until the end of the coroutine body
GlobalScope.launch Start a new thread, create and run coroutines on the new thread, without blocking the current thread
GlobalScope.vinegar Start a new thread, create and run coroutines on the new thread, and do not block the current thread, and support getting the return value through await

Guess you like

Origin blog.csdn.net/qq36246172/article/details/129286862