是协程不是携程

 

看看协程(coroutine)!

至少上网要用到它

简单的用法

一般来说协程比线程好使,协程里可以用delay挂起,和线程的sleep挺不同

kt里边能开协程的方式挺多

比如

GlobalScope.launch {
    println("codes run in coroutine scope")
    delay(1500) 
    println("Ciallo~(∠·ω< )⌒★")
}

delay只会挂起这个协程,不像sleep会祸及自身与包含的协程

再如

runBlocking { 
    launch { 
        println("launch1")
        delay(1000)
        println("launch1 finished")
        }
    launch { 
        println("launch2")
        delay(1000)
        println("launch2 finished")
    }
}

runBlocking如果不跑完,那父线程也不会结束

在协程里边可以用很多个launch来开很多个协程

当然如果不是像runBlocking这种东西外边的线程(协程)结束了那里边的协程也会结束

挂起函数

在函数前面加个suspend就是挂起(暂停)函数,挂起函数里面也可以用delay来咋瓦鲁多

比如

suspend fun printDot() {
    println(".")
    delay(1000)
    println(".")
}

但是如果不在协程里面是不能delay的,所以可以让它继承自coroutineScope这个会创建一个协程的函数,变成这样(coroutineScoperunBlocking一样自己不结束上一级也不结束)

suspend fun printDot() = coroutineScope {
    println(".")
    delay(1000)
    println(".")
}

然后这玩意放哪都能生效了

线程参数

对于大多数启动协程的方法而言,这个参数不是必须的,只有withContext()强制需要

添加这个参数就能添加这个参数(废话

CoroutineScope(Dispatchers.IO)让协程在IO线程池中进行

CoroutineScope(Dispatchers.Main)可以在主线程执行UI相关操作(这是Android独享)

CoroutineScope(Dispatchers.Default)则是一种默认的低并发的策略

创建协程的常用方法

GlobalScope会创建一个与应用同寿的协程,实际中这样用会出现警告波浪线

lifecycleScope会创建一个与现有Activity或Fragment同寿的协程

viewModelScope则是创建和ViewModel同寿的协程

CoroutineScope可以创建自定义的作用域

CoroutineScope

事实上上面的launch会返回一个Job对象,用来管理这个协程的一些关系,也可以拿来取消这个协程,比如

val job = GlobalScope.launch  { 
	// 我不是代码
} 
job.cancel()

然后就会有人发现:每个协程都要一个Job的话,那协程一多起来不得麻烦死

于是为了解决这种神奇的麻烦问题,可以像下面这样用Job

val job = Job() 
val scope1 = CoroutineScope(job) // 在job的作用域里
val scope2 = CoroutineScope(job) // 也在job的作用域里
scope1.launch { 
	// 我也不是代码
} 
scope2.launch { 
	// 我说我是代码你信吗
} 
job.cancel() // 全部取消

这样可就方便多了,而且如果你想的话,还可以这样写

val myScope = CoroutineScope(Job() + Dispatchers.Default)

async?好眼熟的东西

launch一样,async也可以拿来启动一个协程,但只能在协程里面启动

async最大的特色就是可以搭配await()来获得其启动的协程的返回值,由于要获得返回值,所以await一定要等对应的协程跑完才会让你继续跑

下面就是书上的某个例子

runBlocking { 
	val start = System.currentTimeMillis() 
	val result1 = async { 
		delay(1000) 
		5 + 5 
	}.await() 
	val result2 = async { 
		delay(1000) 
		4 + 6 
	}.await() 
	println("result is ${result1 + result2}.") 
	val end = System.currentTimeMillis() 
	println("cost ${end - start} ms.")
}

最终的运行结果是超过2000ms的,因此每个await()都会等一秒钟

改成这样就一共只要一秒钟了:

runBlocking { 
	val start = System.currentTimeMillis() 
	val result1 = async { 
		delay(1000) 
		5 + 5 
	}
	val result2 = async { 
		delay(1000) 
		4 + 6 
	}
	println("result is ${deferred1.await() + deferred2.await()}.")
	val end = System.currentTimeMillis() 
	println("cost ${end - start} ms.")
}

显然都在print那里await()了,真是省时省事

withContext()

这玩意在效果上就是async的简化版

runBlocking { 
	val result = withContext(Dispatchers.Default) { 
		5 + 5 
	} 
	println(result) 
}

相当于

runBlocking {
	val result = async{ 
		5 + 5 
	}.await()
	println(result) 
}

如此而已