kotlin之协程(三),开始创建协程,launch,withContext

目录

kotlin之协程(一),线程,进程,协程,协程可以替换线程吗?
kotlin之协程(二),Kotlin协程是什么、挂起是什么、挂起的非阻塞式
kotlin之协程(三),开始创建协程,launch,withContext
kotlin之协程(四),协程的核心关键字suspend
kotlin之协程(五),launch 函数以及协程的取消与超时
kotlin之协程(六),协程中的 async和launch的区别以及runBlocking
kotlin之协程(七),协程中relay、yield 区别

前言

在开始做安卓之前都是学习如何用框架,用一个库,但是在用的过程中不了解核心本质,无法理解其含义.
后来就换了学习的方法
在用一个东西之前,都是先了解这个事物的本质,再去学习如何用,这样的形式可能在用的过程中有更深的体会.在使用的过程中再去更深入的了解其如何实现.

我们想在kotlin使用协程

项目中配置对 Kotlin 协程的支持

在使用协程之前,我们需要在 build.gradle 文件中增加对 Kotlin 协程的依赖:

  • 项目根目录下的 build.gradle :
buildscript {

    ext.kotlin_coroutines = '1.4.0'

}
  • Module 下的 build.gradle
dependencies {

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.0'

}

创建协程

kotlin 中 GlobalScope 类提供了几个携程构造函数:

  • launch - 创建协程
  • async - 创建带返回值的协程,返回的是 Deferred 类
  • withContext - 不创建新的协程,指定协程上运行代码块
  • runBlocking - 不是 GlobalScope 的 API,可以独立使用,区别是 runBlocking 里面的 delay 会阻塞线程,而 launch 创建的不会

先跑起来一个简单的例子:

import kotlinx.coroutines.*

fun main() {
    GlobalScope.launch { // 在后台启动一个新的协程并继续
        delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
        println("World!") // 在延迟后打印输出
    }
    println("Hello,") // 协程已在等待时主线程还在继续
    Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
}

协程最简单的使用方法,其实在前面章节就已经看到了。我们可以通过一个 launch 函数实现线程切换的功能

coroutineScope.launch(Dispatchers.IO) {
    ...
}

所以,什么时候用协程?当你需要切线程或者指定线程的时候。你要在后台执行任务?切!

launch(Dispatchers.IO) {
    val image = getImage(imageId)
}

然后需要在前台更新界面?再切!

coroutineScope.launch(Dispatchers.IO) {
    val image = getImage(imageId)
    launch(Dispatchers.Main) {
        avatarIv.setImageBitmap(image)
    }
}

好像有点不对劲?这不还是有嵌套嘛。

如果只是使用 launch 函数,协程并不能比线程做更多的事。不过协程中却有一个很实用的函数:withContext 。这个函数可以切换到指定的线程,并在闭包内的逻辑执行结束之后,自动把线程切回去继续执行。那么可以将上面的代码写成这样:

coroutineScope.launch(Dispatchers.Main) {      //  在 UI 线程开始
    val image = withContext(Dispatchers.IO) {  // 切换到 IO 线程,并在执行完成后切回 UI 线程
        getImage(imageId)                      // 将会运行在 IO 线程
    }
    avatarIv.setImageBitmap(image)             // 回到 UI 线程更新 UI
} 

我们甚至可以把 withContext 放进一个单独的函数里面:

launch(Dispatchers.Main) {              //  在 UI 线程开始
    val image = getImage(imageId)
    avatarIv.setImageBitmap(image)     //  执行结束后,自动切换回 UI 线程
}
//                              
fun getImage(imageId: Int) = withContext(Dispatchers.IO) {
    ...
}

这就是之前说的「用同步的方式写异步的代码」了。

不过如果只是这样写,编译器是会报错的:

fun getImage(imageId: Int) = withContext(Dispatchers.IO) {
    // IDE 报错 Suspend function'withContext' should be called only from a coroutine or another suspend funcion
}

意思是说,withContext 是一个 suspend 函数,它需要在协程或者是另一个 suspend 函数中调用。
what?

suspend函数我们在kotlin之协程(二),Kotlin协程是什么、挂起是什么、挂起的非阻塞式有提到,下一章我们专门讲解kotlin协程中的suspend

(每天学习一点点.每天进步一点点,分享不宜路过点个赞呀,喜欢的点个关注后续更新不断)

你可能感兴趣的:(kotlin之协程(三),开始创建协程,launch,withContext)