Android开发kotlinkotlin学习与进阶

全民 Kotlin:协程特别篇

2022-01-08  本文已影响0人  Android轮子哥

目录

什么是协程

A coroutine is an instance of suspendable computation. It is conceptually similar to a thread, in the sense that it takes a block of code to run that works concurrently with the rest of the code. However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one.

Coroutines can be thought of as light-weight threads, but there is a number of important differences that make their real-life usage very different from threads.

一个协同程序是悬浮计算的一个实例。它在概念上类似于线程,从某种意义上说,它需要一个与其余代码同时运行的代码块来运行。但是,协程不受任何特定线程的约束。它可以在一个线程中暂停其执行并在另一个线程中恢复。

协程可以被认为是轻量级线程,但是有许多重要的区别使得它们在现实生活中的使用与线程大不相同。

suspend 关键字介绍

suspend fun getUserName(userId: Int): String {
    delay(20000)
    return "Android 轮子哥"
}

集成协程

dependencies {
    // Kotlin 协程:https://github.com/Kotlin/kotlinx.coroutines
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
}

runBlocking 用法

println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
runBlocking {
    println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
12:53:15.799 System.out: 测试开始 true
12:53:15.811 System.out: 测试延迟开始 true
12:53:35.814 System.out: 测试延迟结束
12:53:35.815 System.out: 测试结束

Suspend function 'delay' should be called only from a coroutine or another suspend function

挂起函数 'delay' 应该只从协程或另一个挂起函数调用

launch 用法

println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
GlobalScope.launch {
    println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
17:19:17.190 System.out: 测试开始 true
17:19:17.202 System.out: 测试结束
17:19:17.203 System.out: 测试延迟开始 false
17:19:37.223 System.out: 测试延迟结束

async 用法

println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
GlobalScope.async {
    println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
17:29:00.694 System.out: 测试开始 true
17:29:00.697 System.out: 测试结束
17:29:00.697 System.out: 测试延迟开始 false
17:29:20.707 System.out: 测试延迟结束
println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
val async = GlobalScope.async {
    println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
    delay(20000)
    println("测试延迟结束")
    return@async "666666"
}
println("测试结束")
println("测试返回值:" + async.await())
17:50:57.117 System.out: 测试开始 true
17:50:57.120 System.out: 测试结束
17:50:57.120 System.out: 测试延迟开始 false
17:51:17.131 System.out: 测试延迟结束
17:51:17.133 System.out: 测试返回值:666666

协程的线程调度器

println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
GlobalScope.launch(Dispatchers.Main) {
    println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
18:00:23.244 System.out: 测试开始 true
18:00:23.246 System.out: 测试结束
18:00:23.247 System.out: 测试延迟开始 true
18:00:43.256 System.out: 测试延迟结束
println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
GlobalScope.launch(Dispatchers.Main) {
    withContext(Dispatchers.IO) {
        println("测试是否为主线程 " + (Thread.currentThread() == Looper.getMainLooper().thread))
    }
    println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
    delay(20000)
    println("测试延迟结束")
}
println("测试结束")
22:21:21.355 System.out: 测试开始 true
22:21:21.367 System.out: 测试结束
22:21:21.390 System.out: 测试是否为主线程 false
22:21:22.058 System.out: 测试延迟开始 true
22:21:42.059 System.out: 测试延迟结束
suspend fun getUserName(userId: Int): String  {
    return withContext(Dispatchers.IO) {
        delay(20000)
        return@withContext "Android 轮子哥"
    }
}
suspend fun getUserName(userId: Int): String = withContext(Dispatchers.IO) {
    delay(20000)
    return@withContext "Android 轮子哥"
}

协程的启动模式

val job = GlobalScope.launch(Dispatchers.Default, CoroutineStart.LAZY) {

}
job.start()

协程设置执行超时

GlobalScope.launch() {
    try {
        withTimeout(300) {
            // 重复执行 5 次里面的内容
            repeat(5) { i ->
                println("测试输出 " + i)
                delay(100)
            }
        }
    } catch (e: TimeoutCancellationException) {
        // TimeoutCancellationException 是 CancellationException 的子类
        // 其它的不用我说了吧?文章前面有介绍 CancellationException 类作用的
        println("测试协程超时了")
    }
}
23:52:48.415 System.out: 测试输出 0
23:52:48.518 System.out: 测试输出 1
23:52:48.618 System.out: 测试输出 2
23:52:48.715 System.out: 测试协程超时了
GlobalScope.launch() {
    val result = withTimeoutOrNull(300) {
        // 重复执行 5 次里面的内容
        repeat(5) { i ->
            println("测试输出 " + i)
            delay(100)
        }
        return@withTimeoutOrNull "执行完成了"
    }
    println("测试输出结果 " + result)
}
23:56:02.462 System.out: 测试输出 0
23:56:02.569 System.out: 测试输出 1
23:56:02.670 System.out: 测试输出 2
23:56:02.761 System.out: 测试输出结果 null
23:59:37.288 System.out: 测试输出 0
23:59:37.390 System.out: 测试输出 1
23:59:37.491 System.out: 测试输出 2
23:59:37.591 System.out: 测试输出 3
23:59:37.692 System.out: 测试输出 4
23:59:37.793 System.out: 测试输出结果 执行完成了

协程的生命周期控制

This is a delicate API and its use requires care. Make sure you fully read and understand documentation of the declaration that is marked as a delicate API.

这是一个微妙的API,使用时需要小心。确保您充分阅读并理解标记为敏感API的声明的文档。

A global [CoroutineScope] not bound to any job.

Global scope is used to launch top-level coroutines which are operating on the whole application lifetime and are not cancelled prematurely.

Active coroutines launched in GlobalScope do not keep the process alive. They are like daemon threads.

This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used. A coroutine launched in GlobalScope is not subject to the principle of structured concurrency, so if it hangs or gets delayed due to a problem (e.g. due to a slow network), it will stay working and consuming resources.

全局的[CoroutineScope]不绑定到任何作业。

全局作用域用于启动顶级协程,这些协程在整个应用程序生命周期中运行,不会提前取消。

在“GlobalScope”中启动的活动协程不会使进程保持活动状态。它们就像守护线程。

这是一个非常精致的API。当使用' GlobalScope '时,很容易意外地创建资源或内存泄漏。在“GlobalScope”中启动的协程不受结构化并发原则的约束,所以如果它挂起或由于问题(例如由于网络缓慢)而延迟,它将继续工作并消耗资源。

dependencies {
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
}
class TestActivity : AppCompatActivity() {

    fun test() {
    
        lifecycleScope.launch {
            
        }
    }
}
class TestViewModel : ViewModel() {
    
    fun test() {
    
        viewModelScope.launch() {

        }
    }
}
val launch = GlobalScope.launch() {
    
}
launch.cancel()
println("测试开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
val job = GlobalScope.launch() {
    try {
        println("测试延迟开始 " + (Thread.currentThread() == Looper.getMainLooper().thread))
        delay(20000)
        println("测试延迟结束")
        delay(20000)
        println("测试延迟结束")
    } catch (e: CancellationException) {
        // 在这里可以添加一个 try catch 来捕获取消的动作
        // 另外需要注意的是,如果在协程体内发生 CancellationException 异常
        // 会被协程内部自动消化掉,并不会导致应用程序崩溃的
        // 所以一般情况下不需要捕获该异常,除非需要手动释放资源
        println("测试协程被取消了")
    }
}
println("测试结束")

// 手动取消协程
job.cancel()
12:35:10.005 System.out: 测试开始 true
12:35:10.022 System.out: 测试结束
12:35:10.023 System.out: 测试延迟开始 false
12:35:10.027 System.out: 测试协程取消了

另外推荐一个 Kotlin 语言编写的开源项目,大家感兴趣可以看看:AndroidProject-Kotlin

Android 技术讨论 Q 群:10047167

上一篇下一篇

猜你喜欢

热点阅读