【11】Kotlin之协程(一),协程,挂起,协程的线程

协程是Kotlin引入的新概念,用于简化异步编程,避免回调地狱。它们是轻量级线程,启动和切换成本低,能在单个线程上运行多个。协程支持挂起功能,不会阻塞线程,通过suspend函数实现。使用Dispatchers可以指定协程运行的线程,如Main、IO或Default。launch用于启动协程,async则用于启动带返回结果的协程。
摘要由CSDN通过智能技术生成

前言

协程(Coroutine),Kotlin引入的新的概念,co表示协同、协作,routine表示程序。协程义在多个互相协作的程序。

协程

协程是轻量级的线程,这可能很多人都知道,但是协程的轻量级体现在哪里呢?就是启动和切换,协程的启动不需要申请额外的退栈空间;协程的切换发生在用户态,而非内核态,避免了系统的复杂的调度,也可以说,协程就是一个封装在线程上面的线程框架

特点
  • 轻量:可以在单个线程上运行多个协程,因为协程支持挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节省内存,且支持多个并行操作。
  • 内存泄漏更少:使用结构化并发机制在一个作用域内执行多项操作。
  • 内置取消支持:取消操作会自动在运行中的整个协程层次结构内传播。
作用

协程通过替代回调callback来简化异步代码

	fun fetchDocs() {
		val result = get("developer.android.com")
		show(result)
	}

在这里插入图片描述
我们都知道,Android为了保证界面的流畅和及时响应,主线程需要保持每16ms刷新一次,所以我们不能在主线程去做耗时的操作。
上图中get()通过接口去获取数据,如果主线程中去调用fetchDocs()函数就会阻塞(block)主线程,所以创建一个子线程去处理get()方法,待get()函数执行完毕后通过callback拿到结果。

	fun fetchDocs(){
		get("developer.android.com") { result ->
			show(result)
		}
	}

在这里插入图片描述
callback 是个不错的方式,但是可能你会遇到下面这种情况,使得代码的可读性变得很差。

	fun fetchDocs(){
		get("developer.android.com"){
			override fun success(result: String){
				show(result)
			}
			override fun failure(msg: String){
				//我希望在失败的过程中根据msg的状态重新去获取数据
				get("developer.android.com") {
					//......
				}
			}
		}
	}

为了解决这样的问题,协程(coroutine)就出现了。

	suspend fun fetchDocs() {
		val result = get("developer.android.com")
		show(result)
	}
	suspend fun get(url: String) =
		withContext(Dispatcher.IO) {
			//...
		}

在这里插入图片描述
一眼看过去,除了多了suspend修饰方法,其他流程都没有变化,为什么不会阻塞主线程呢?
被suspend修饰的函数比普通函数多了两个操作(suspend和resume

  • suspend:暂停当前协程的执行,保存所有的局部变量
  • resume:从协程被暂停的地方继续执行协程
//举个例子
	fun main() {
		println("main before")
		coroutineScope(Dispather.IO).launch {
			println("before")
			// get是挂起函数,会暂停当前协程的执行,保存所有局部变量。主线程会继续执行
			val docs = get("developer.android.com")
			// get执行完毕,resume从协程被暂停的地方继续执行协程
			println("after")
		}
		println("main after")
	}
	
	suspend fun get(url: String) =
		withContext(Dispatchers.IO) {
			//...
		}

suspend修饰的函数并不意味着运行在子线程中

协程的组成

取消协程,又称为协程的取消机制,这是官方的示例代码。

suspend fun main(): Unit = coroutineScope {
    val job = launch {
        repeat(1000) { i ->
            println("job: I'm sleeping $i ...")
            delay(500L)
        }
    }
    delay(1300L) // delay a bit
    println("main: I'm tired of waiting!")
    job.cancel() // cancels the job
    job.join() // waits for job's completion 
    println("main: Now I can quit.")
}

协程的延时

suspend fun main() {
	delay(1000L) //delay是一个suspend函数
	println("quit.")
}

指定协程线运行的线程
如果需要指定协程运行的线程,就需要指定Dispathers,常用的有三种

  • Dispatchers.Main:Android中的主线程,可以直接操作UI。
  • DIspatchers.IO:针对磁盘和网络IO进行了优化,适合UI密集型的任务(比如读写文件、操作数据库、网络请求)。
  • DIspatchers.Default:适合CPU密集型的任务,比如解析JSON文件,排序数据量大的操作。

通过withContext()可以指定Dispatchers,这里的get()函数里的withContext代码块中指定了协程运行在Dispatchers.IO中。

launch 和 async

  • launch 启动一个协程,返回一个Job,可用来取消协程;有异常直接抛出
  • async 启动一个带返回结果的协程,可以通过Deferred.await()获取结果;有异常并不会直接抛出,只会在调用 await 的时候抛出
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值