1、协程的概念
相当于轻量级线程,线程的切换需要操作系统进行调度,而协程在编译语言层面就能实现自由切换,开销比线程要小的多,这种特性使得高并发程序的执行效率得到了极大的提升
2、协程的优点
可控制、开销小、语法糖(减少回调,已同步的写法代替之前异步的写法)
协程与线程是属于一个层级的,但是却又和线程处理并行任务有着不同的解决方案,协程可以在处理完并发任务之后,自动切回UI(mian/主)线程;线程处理完并发任务默认是不会直接切回UI(mian/主)线程,需要我们手动去切回UI(mian/主)线程,因此,协程不仅切换成本小,还可以避免回调地狱
3、协程的用法
首先引入依赖
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.1.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.1"
GlobalScope.launch
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
GlobalScope.launch {
delay(1500)
println("codes run in coroutine scope")
}
sleep(1000)
}
}
GlobalScope.launch创建的协程是顶层协程,返回值时Job,会随着线程的销毁而销毁,因此需要sleep 1s,delay只能用于协程作用域或挂起函数里,delay在协程中调用时只会阻塞当前协程,不会影响其它协程
runBlocking
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
runBlocking {
launch {
delay(1000)
println("codes run in runblocking")
}
}
}
}
runBlocking 可以阻塞当前线程直到其中的所有协程全部执行完成,一般在测试环境使用
挂起函数和coroutineScope
当协程内代码过多时,我们可以把其中一部分代码抽出来写成一个方法,但如果需要在方法中使用delay方法时,需要把这个方法声明成挂起函数,用suspend修饰,不过现在还是不能使用launch,因为挂起函数不是协程作用域,如果想在挂起函数中使用launch方法,可以用coroutineScope函数使挂起函数拥有协程作用域,如下:
//挂起函数中使用delay方法
suspend fun method1(){
delay(1000)
println("test fdfda")
}
//使用coroutineScope声明一个带协程作用域的方法
suspend fun method2()= coroutineScope{
launch {
println("method2")
}
}
coroutineScope会阻塞当前协程,直到它内部的所有协程都执行完
在实际项目中,由于runBlocking可能会阻塞当前线程,一般不用,而Global.launch这种顶层协程每个会返回一个Job,管理起来比较麻烦,所以一般也不使用,下面介绍一种项目中常用的协程使用
val job=Job()
val scope= CoroutineScope(job)
scope.launch {
println("111111")
delay(100)
println("111111")
}
scope.launch {
println("22222")
delay(100)
println("22222")
}
//job.cancel()
sleep(1000)
使用Job可统一取消所有通过CoroutineScope启动的协程(这种和顶层协程一样,不会阻塞线程,随线程销毁而结束),因为launch函数只能返回job对象,那么如果我们需要获取一个协程的返回结果时,就得使用async函数了
能获取协程结果的async函数
async必须在协程作用域才能调用,它会创建一个新的协程并返回一个Deferred对象,调用Deferred对象的await方法即可获取协程的结果,如下:
//能获取协程结果的async函数
runBlocking {
val result=async {
5+6
}.await()
println(result)
}
//await会阻塞当前协程(不影响其它协程),直到可以获取到async的结果
runBlocking {
val result=async {
sleep(1000)
println("第一个async函数")
5+6
}.await()
async {
println("第二个async函数")
}.await()
println(result)
}
//async函数同时执行
runBlocking {
val start=System.currentTimeMillis()
val deferred1=async {
delay(1000)
println("第一个async函数")
}
val deferred2=async {
delay(1000)
println("第二个async函数")
}
deferred1.await()
deferred2.await()
println(System.currentTimeMillis()-start)
强制要求指定线程参数的协程
//强制要求指定线程的协程
runBlocking {
withContext(Dispatchers.Default){
delay(1000)
println("withContext"+Thread.currentThread().id)
}
}
println("主线程id=${Thread.currentThread().id}")
共有三种类型,分别是Dispatchers.Default(适用于低并发)、Dispatchers.IO(适用于高并发)、Dispatchers.Main(不会开启子线程,会在主线程执行)
协程简化回调
//常见的网络请求回调
HttpUtil.sendHttpRequest(url,object:HttpCallbackListener{
override fun onFinish(response:String){
//得到服务器返回的具体内容
}
override fun onError(e:Exception){
//异常情况处理
}
})
//使用协程简化回调
suspend fun request(url:String):String{
return suspendCoroutine { continuation ->
HttpUtil.sendHttpRequest(url,object:HttpCallbackListener{
override fun onFinish(response:String){
//得到服务器返回的具体内容
continuation.resume(response)
}
override fun onError(e:Exception){
//异常情况处理
continuation.resumeWithException(e)
}
})
}
}
//后续网络请求可直接这么写
try{
val response=request("xxxx")
//对服务器响应的数据进行处理
}catch(e:Exception){
//对异常情况进行处理
}
网友评论