看看协程(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
这个会创建一个协程的函数,变成这样(coroutineScope
像runBlocking
一样自己不结束上一级也不结束)
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)
}
如此而已