前言
协程是并发编程技术,允许在一个线程执行多个任务,不需要创建多个线程。线程是操作系统概念,协程是编程语言概念。协程可以暂停和恢复执行,线程只能被终止。 CoroutineContext定义了协程的执行环境。
CoroutineContext概念
是一个容器,包含了协程上下文信息。
- 协程状态:生命周期。Active Completed Canceled
- 协程调度策略:决定协程在哪里执行。主线程 后台线程 其他线程池
- 协程拦截器:拦截协程的执行流程
- 协程异常捕获:处理协程内部发生未捕获的异常
fun main() {
runBlocking {
// 协程上下文环境
println(coroutineContext)
}
}
输出:
// 默认情况下三部分组成:协程id,协程的Job,协程的dispatcher
[CoroutineId(1), "coroutine#1":BlockingCoroutine{Active}@135606db, BlockingEventLoop@518caac3]
CoroutineContext组成
由多个组件组成,通过context.get<T>()函数获取.
由于重新定义了get操作符,可以通过context[key]获取上下文组件元素。
fun main() = runBlocking {
val context = coroutineContext//+CoroutineName("张三") //+ Dispatchers.IO
val dispatcher = context[ContinuationInterceptor]
val job = context[Job]
val name = context[CoroutineName]
println("context:$context")
println("job:$job")
println("dispatcher:$dispatcher")
println("name:$name")
}
输出:
context:[CoroutineId(1), "coroutine#1":BlockingCoroutine{Active}@59d4cd39, BlockingEventLoop@389c4eb1]
job:"coroutine#1":BlockingCoroutine{Active}@59d4cd39
dispatcher:BlockingEventLoop@389c4eb1
name:null
fun main() = runBlocking {
val context = coroutineContext + CoroutineName("张三") + Dispatchers.IO + SupervisorJob()
val dispatcher = context[ContinuationInterceptor]
val job = context[Job]
val name = context[CoroutineName]
println("context:$context")
println("job:$job")
println("dispatcher:$dispatcher")
println("name:$name")
}
输出:
context:[CoroutineId(1), CoroutineName(张三), SupervisorJobImpl{Active}@6e15fe2, Dispatchers.IO]
job:SupervisorJobImpl{Active}@6e15fe2
dispatcher:Dispatchers.IO
name:CoroutineName(张三)
- Dispatcher:协程的调度策略
- Job:协程的状态。表示协程的生命周期
fun main() = runBlocking {
val context = coroutineContext + SupervisorJob()
val job = context[Job]
if (job?.isActive == true) {
println("协程处于Active状态")
}
}
输出:
协程处于Active状态
添加拦截器:协程执行前、执行中、执行后操作
class MyContinuationInterceptor:ContinuationInterceptor{
override val key: CoroutineContext.Key<ContinuationInterceptor>
get() = ContinuationInterceptor.Key
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> {
println("协程开始之前")
return continuation
}
}
fun main() {
runBlocking {
launch(Dispatchers.IO+MyContinuationInterceptor()) {
println("协程开始执行")
delay(1000)
}
}
}
输出:
协程开始之前
协程开始执行
CoroutineExceptionHandler
处理协程内部发生的未捕获异常
fun main(){
println("test main")
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("Caught an exception: $exception")
}
GlobalScope.launch(exceptionHandler) {
println("test launch1")
launch {
println("test launch-> threadName->${Thread.currentThread().name}")
throw NullPointerException()
}
}
Thread.sleep(2000)
println("test end")
}
输出:
test main
test launch1
test launch-> threadName->DefaultDispatcher-worker-2 @coroutine#2
Caught an exception: java.lang.NullPointerException
test end
增加了协程内部抛出的异常,会被CoroutineExceptionHandler捕获(上面的协程虽然launch两次但是在同一个域内,可以捕获到异常)。
fun main(){
println("test main")
GlobalScope.launch(CoroutineExceptionHandler { _, throwable ->
println("MainActivity_${throwable.message.toString()}")
}) {
GlobalScope.launch {
println("MainActivity_ launch-> threadName->${Thread.currentThread().name}")
// 异常不被CoroutineExceptionHandler捕获
throw NullPointerException()
}
}
Thread.sleep(2000)
println("test end")
}
输出:
test main
MainActivity_ launch-> threadName->DefaultDispatcher-worker-2 @coroutine#2
Exception in thread "DefaultDispatcher-worker-2 @coroutine#2" java.lang.NullPointerException.....
test end
CoroutineExceptionHandler只能处理当前域内开启的子线程或当前协程抛出的异常。所以上面父域无法捕获子域额异常的(在不同的域内,所以无法捕获到异常)。
fun main() {
println("test main")
val scope = CoroutineScope(Job())
scope.launch {
launch(CoroutineExceptionHandler { _, _ -> println("test catch") }) {
delay(10)
// 异常不被捕获
throw RuntimeException()
}
}
Thread.sleep(2000)
println("test end")
}
输出:
test main
Exception in thread ....
test end
当子协程发生异常时,会优先将异常委托给父协程区处理,直到根协程作用域或顶级协程。因此永远不会使用我们子协程(CoroutineExceptionHandler(SupervisorJob除外))。
import kotlinx.coroutines.*
fun main() {
// 创建CoroutineExceptionHandler
val exceptionHandler = CoroutineExceptionHandler { _, exception ->
println("Caught an exception: $exception")
}
runBlocking {
val context = coroutineContext + exceptionHandler
val job = GlobalScope.launch(context) {
println("Coroutine is doing some work")
delay(1000)
// 异常无法被捕获;当子协程发生异常时,会优先将异常委托给父协程区处理
throw CustomException("Something went wrong!")
}
// 等待协程执行结束
job.join()
}
}
// 自定义异常类
class CustomException(message: String) : Exception(message)
上面无法捕获到异常。
总结
CoroutineContext来控制协程的执行,合理选择调度器,细致管理,以及异常处理。