Kotlin学习笔记——(十一)协程的基本使用

注:编码工具为IntelliJ

目录

依赖

启动

几种启动方式的区别

阻塞与非阻塞

返回值

自定义作用域


依赖

使用协程前,需要导入协程依赖。

implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.2'

Android中需要额外导入依赖。

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

启动

package step_twelve

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import java.text.SimpleDateFormat

private fun Long.date() = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this)

private fun log(msg: String){
    println("[${System.currentTimeMillis().date()}]-[${Thread.currentThread().name}] $msg")
}
private class StartCoroutines{
    fun t(){
        runBlocking {
            log("runBlocking 方式启动一个协程")
        }

        GlobalScope.launch {
            log("launch 方式启动一个协程")
        }

        GlobalScope.async {
            log("async 方式启动一个协程")
        }
    }
}
fun main() {
    StartCoroutines().t()
}

输出:

[2021-11-24 10:42:48]-[main] runBlocking 方式启动一个协程
[2021-11-24 10:42:48]-[DefaultDispatcher-worker-1] launch 方式启动一个协程
[2021-11-24 10:42:48]-[DefaultDispatcher-worker-1] async 方式启动一个协程

        可以从输出看出,runBlocking方式启动协程直接在协程启动的线程中执行协程体,而launch和async方式启动协程则另外开辟一个线程执行协程体。

几种启动方式的区别

阻塞与非阻塞

package step_twelve

import kotlinx.coroutines.*

fun main() {
    log("before")
    runBlocking {
        delay(2000)
        log("runBlocking")
    }

    GlobalScope.launch {
        delay(2000)
        log("launch")
    }
    GlobalScope.async {
        delay(2000)
        log("async")
    }
    log("after")
}

输出:

[2021-11-24 10:56:26]-[main] before
[2021-11-24 10:56:29]-[main] runBlocking
[2021-11-24 10:56:29]-[main] after

        delay函数相当于Thead的sleep。

        从输出可以看出,runBlocking阻塞了当前线程,先睡眠2秒,然后才继续执行协程体,执行结束继续执行main函数的后续代码,而launch和async因为不是阻塞式协程,启动后,main函数后续代码和协程体会同时执行,main函数因为没有睡眠,先执行结束了,而launch和async还在睡眠,所以log没有输出。

        如果想要launch和async先执行完,然后main函数才执行完,可以让main函数睡眠一会。

package step_twelve

import kotlinx.coroutines.*

fun main() {
    log("before")
    runBlocking {
        delay(2000)
        log("runBlocking")
    }

    GlobalScope.launch {
        delay(2000)
        log("launch")
    }
    GlobalScope.async {
        delay(2000)
        log("async")
    }
    Thread.sleep(3000)
    log("after")
}

输出:

[2021-11-24 11:04:35]-[main] before
[2021-11-24 11:04:37]-[main] runBlocking
[2021-11-24 11:04:39]-[DefaultDispatcher-worker-2] launch
[2021-11-24 11:04:39]-[DefaultDispatcher-worker-1] async
[2021-11-24 11:04:40]-[main] after

返回值

package step_twelve

import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

suspend fun main() {
    println("runBlocking 返回值是 ${runBlocking { log("runBlocking") }}")
    println("runBlocking 返回值是 ${runBlocking { "runBlocking" }}")
    println("launch 返回值是 ${GlobalScope.launch { log("launch") }}")
    println("launch 返回值是 ${GlobalScope.launch { "launch" }}")
    println("async 返回值是 ${GlobalScope.async { log("async") }}")
    println("async 返回值是 ${GlobalScope.async { "async" }}")
    println("async 返回值是 ${(GlobalScope.async { "async" }).await()}")
}

输出:

[2021-11-24 11:12:17]-[main] runBlocking
runBlocking 返回值是 kotlin.Unit
runBlocking 返回值是 runBlocking
[2021-11-24 11:12:17]-[DefaultDispatcher-worker-1] launch
launch 返回值是 StandaloneCoroutine{Active}@53e25b76
launch 返回值是 StandaloneCoroutine{Active}@73a8dfcc
async 返回值是 DeferredCoroutine{Active}@1b701da1
[2021-11-24 11:12:17]-[DefaultDispatcher-worker-1] async
async 返回值是 DeferredCoroutine{Active}@726f3b58
async 返回值是 async

        从输出可以看出,runBlocking协程体的最后一行执行结果就是该方式启动的协程的返回值,而launch和async方式启动的协程默认返回协程对象。async方式启动的协程返回的对象可以通过await方法获得该协程的最后一行执行结果,这也是async和launch不同的地方。

小结:

        runBlocking方式启动的协程会阻塞当前线程,launch和async方式启动的协程则不会;

        runBlocking方式启动的协程的协程体最后一行执行结果就是协程的返回值,launch和async方式启动的协程默认会返回一个协程对象,async方式启动的协程返回的协程对象可以通过await函数获得返回值。

        要调用async方式启动的协程返回的协程对象的await函数,主调函数必须声明为suspend的,所以上面代码中,main函数被声明为suspend的。

自定义作用域

        协程必须有作用域才可以运行,可以自定义协程的作用域。

package step_twelve

import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.launch
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.EmptyCoroutineContext

fun main() {
    val scope = CoroutineScope(EmptyCoroutineContext)
    scope.launch {
        log("自定义作用域")
    }
    val scope2 = object : CoroutineScope{
        override val coroutineContext: CoroutineContext
            get() = EmptyCoroutineContext
    }
    scope2.async {
        log("自定义作用域")
    }
    Thread.sleep(100)
}

输出:

[2021-11-24 11:26:12]-[DefaultDispatcher-worker-2] 自定义作用域
[2021-11-24 11:26:12]-[DefaultDispatcher-worker-1] 自定义作用域

        这里仿照GlobalScope自定义了Scope,具体自定义作用域有什么用处,还有待探索。

总结:

        runBlocking主要用于测试,该函数的设计目的是让用suspend风格编写的库能够在常规阻塞代码中使用,常在main函数和测试代码中使用。

        GlobalScope.launch/async不建议使用,因为这种启动方式启动的协程的生命周期是全局的,存在启动协程的组件已经被销毁但协程依然存在的情况,容易发生内存泄漏和内存溢出问题,尤其是移动应用这种需要频繁创建销毁组件的场景。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值