kotlin协程在Android 被用来代替线程,可以当做线程框架来使用,主要用于处理异步任务,并发等。
线程是依靠操作系统调度实现的线程切换,协程是编程语言层面的,轻量级的线程。
协程允许我们在单线程模式下模拟多线程的效果
协程创建:
//会阻塞主线程
runBlocking {
/* ... */
}
//全局的单例对象
GlobalScope.launch {
/* ... */
}
/* Android推荐方式
* 使用coroutineContext
* 这样在activity 里就可以使用 job来实现对协程的统一处理 栗如 :job.cancel() 等
*
*/
var job = Job()
val coroutineScope = CoroutineScope(job)
coroutineScope.launch {
/* ... */
}
// viewmodel 里可以直接使用 viewModelScope
viewModelScope.launch{
/* ... */
}
coroutineScope
coroutineScope是一个挂起函数,在协程内部或挂起函数使用,创建一个协程的作用域 作用域内的所有子协程执行完成之前,会阻塞当前协程。
GlobalScope.launch {
Log.e("Scope", "GlobalScope")
coroutineScope {
launch {
delay(2000)
Log.e("Scope", "coroutineScope1")
}
launch {
delay(1000)
Log.e("Scope", "coroutineScope2")
}
}
Log.e("Scope", "GlobalScope2")
}
执行顺序为:
Scope: GlobalScope
Scope: coroutineScope2
Scope: coroutineScope1
Scope: GlobalScope2
取消作用域:
GlobalScope.launch {
Log.e("Scope", "GlobalScope")
launch {
delay(2000)
Log.e("Scope", "coroutineScope1")
}
launch {
delay(1000)
Log.e("Scope", "coroutineScope2")
}
Log.e("Scope", "GlobalScope2")
}
执行顺序为:
Scope: GlobalScope
Scope: GlobalScope2
Scope: coroutineScope2
Scope: coroutineScope1
同一个作用域下的所有子协程是并发运行的,外部协程取消之后,所有子协程都会停止运行
suspendcoroutine
suspendcoroutine挂起函数必须在协程作用域或者挂起函数调用,会立即挂起协程,它传入一个lambda 表达式,提供了
一个 continuation 参数,调用它的 resume() 或者 resumeWithException () 可以恢复协程。
resume(t : T) 需要传入一个参数,并将参数作为 suspendcoroutine{} 函数的返回值, resumeWithException (e : Throwable) 则会抛出异常, try catch 捕获处理即可
GlobalScope.launch {
try {
var str = test()
} catch (e: Exception) {
Log.e("Scope", "${e.message}")
}
}
suspend fun test() = suspendCoroutine<String> {
if (true){
it.resume("success")
} else {
it.resumeWithException(Exception())
}
}
协程取值
async {} 函数在协程作用域内部使用,函数会立即执行,并返回 Deferred 类型,调用 Deferred 的 await() 方法即可得到函数最后一行的值。 调用 await() 方法时,若函数内的代码没执行完,会阻塞当前协程。
GlobalScope.launch {
var a = async { "string" }.await()
}
a == "string"
withContext(Dispatchers){} 函数在协程作用域或者挂起函数中使用,并阻塞当前协程,返回最后一行的值。 传入一个 Dispatchers 值。
GlobalScope.launch {
var a = withContext(Dispatchers.IO){ "string" }
}
a == "string"
Dispatchers
Dispatchers.default 低并发的线程策略,适合计算密集型任务
Dispatchers.io 高并发 适合阻塞等待多的任务
Dispatchers.main Android主线程
除 coroutineScope 以外的挂起函数都可以传入线程参数,withContext 为必传
总结
Globalscope.launch{}、runBlocking {}、 CoroutineScope(job) 可以在任意地方构建协程作用域。
coroutinescope{} 函数在协程作用域和挂起函数构建一个作用域。
launch {} 在协程作用域内部调用,开启一个子协程。
async {} 和 withContext(Dispatchers){} 可以并获取返回值 , async{}.await() 阻塞协程,后者直接阻塞
同一个作用域下的所有子协程是并发运行的,并且生命周期跟随外部协程。
delay
delay 是一个非阻塞式的挂起函数,可以在协程作用域和挂起函数调用