我们使用构建器Launch去启动协程的时候,都需要指定协程上下文(没有显示指定会使用默认值)。
协程上下文(CoroutineContext)是一组用于定义协程的行为元素。它由如下几项构成
job(控制协程的生命周期),CoroutineDispatcher(向合适的线程发任务),CoroutineName(协程的名称,调用的时候很有用),CoroutineExceptionHandler(处理未被捕获的异常)
1,协程上下文的继承
对于新创建的线程,它的CoroutineCoroutineContexttContext包含了一个全新的job实例。它会帮助我们控制协程的生命周期。而剩下的元素会从CoroutineContext的父类继承,该父类可能是另外一个协程,或者创建协程的CoroutineScope。
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{//必须指定泛型类型为 ,不然报错 //创建一个协程作用域,需要指定协程上下文 //有时候需要在协程上下文中定义多个元素,我们可以使用+ *** 作符来实现 val scope = CoroutineScope(Job()+Dispatchers.IO+CoroutineName("test")) val job = scope.launch { //打印出launch启动的协程的Job对象, println("1111111${coroutineContext[Job]} ${Thread.currentThread().name}") val result = async {//launch的子协程 println("22222222${coroutineContext[Job]} ${Thread.currentThread().name}") "OK" }.await() } job.join() } }
打印输出
1111111"test#2":StandaloneCoroutine{Active}@3b1ca9c4 DefaultDispatcher-worker-1 @test#2
22222222"test#3":DeferredCoroutine{Active}@40608051 DefaultDispatcher-worker-3 @test#3
Job对象不一样,是一个全新的实例,其它的从父类继承。打印线程的时候自动会把协程的名字打印出来
2,协程上下文的继承公式
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking协程的异常处理{ val exceptionHandler = CoroutineExceptionHandler { _, exception -> println("caught $exception")//其实是回掉函数 } val scope = CoroutineScope(Job() + Dispatchers.Main+exceptionHandler) val job = scope.launch(Dispatchers.IO) { } } }
代码理解
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import java.lang.ArithmeticException import java.lang.IndexOutOfBoundsException import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{ val job1 = GlobalScope.launch { throw IndexOutOfBoundsException() } job1.join() val job2 = GlobalScope.async { throw ArithmeticException() } job2.await() } }
这里会抛出两个异常,注意这两个异常的catch地方不一样
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import java.lang.ArithmeticException import java.lang.Exception import java.lang.IndexOutOfBoundsException import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{ val job1 = GlobalScope.launch { try { throw IndexOutOfBoundsException() } catch (e: Exception) { e.printStackTrace() } } job1.join() val job2 = GlobalScope.async { throw ArithmeticException() } try { job2.await() } catch (e: Exception) { e.printStackTrace() } } }
非根协程所创建的协程中,产生的异常总会是被传播
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import java.lang.ArithmeticException import java.lang.Exception import java.lang.IllegalArgumentException import java.lang.IndexOutOfBoundsException import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{ val scope = CoroutineScope(Job()) val job = scope.launch { async { //如果async抛出异常,launch就会立即抛出异常,而不会调用.await() throw IllegalArgumentException() } } job.join() } }
一个child失败了,其它的child也取消了。
代码体会第一点
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import java.lang.ArithmeticException import java.lang.Exception import java.lang.IllegalArgumentException import java.lang.IndexOutOfBoundsException import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{ val supervisor = CoroutineScope(SupervisorJob()) //job1泡了异常,job2还会执行 val job1 = supervisor.launch { delay(100) println("111111111111111") throw IllegalArgumentException() } val job2 = supervisor.launch { try { delay(1000) }finally { println("2222222222222") } } joinAll(job1,job2) } }
打印输出
111111111111111 Exception in thread "DefaultDispatcher-worker-1 @coroutine#2" java.lang.IllegalArgumentException at com.example.testkotlin1030.ExampleUnitTest$my test$job1.invokeSuspend(ExampleUnitTest.kt:27) at kotlin.coroutines.jvm.internal.baseContinuationImpl.resumeWith(ContinuationImpl.kt:33) at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) 2222222222222
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import java.lang.ArithmeticException import java.lang.AssertionError import java.lang.Exception import java.lang.IllegalArgumentException import java.lang.IndexOutOfBoundsException import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{ val handler = CoroutineExceptionHandler{_,exception -> println("caught $exception") } val job = GlobalScope.launch (handler){ throw AssertionError()//该异常会被捕获 } val deferred = GlobalScope.async (handler){ throw ArithmeticException() } job.join() deferred.await() } }
异常捕获的错误写法
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import java.lang.ArithmeticException import java.lang.AssertionError import java.lang.Exception import java.lang.IllegalArgumentException import java.lang.IndexOutOfBoundsException import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{ val handler = CoroutineExceptionHandler{_,exception -> println("caught $exception") } val scope = CoroutineScope(Job()) val job = scope.launch { launch (handler){ throw AssertionError() } } job.join() } }
正确的写法应该是
package com.example.testkotlin1030 import kotlinx.coroutines.* import org.junit.Test import org.junit.Assert.* import java.lang.ArithmeticException import java.lang.AssertionError import java.lang.Exception import java.lang.IllegalArgumentException import java.lang.IndexOutOfBoundsException import kotlin.coroutines.CoroutineContext import kotlin.system.measureTimeMillis class ExampleUnitTest { @Test fun `my test`() = runBlocking{ val handler = CoroutineExceptionHandler { _, exception -> println("caught $exception") } val scope = CoroutineScope(Job()) val job = scope.launch(handler) { launch { throw AssertionError() } } job.join() } }
异常处理器不能安装在内部协程,应该安装在外部协程
android 中捕获异常阻止程序闪退
package com.dongnaoedu.kotlincoroutineexception import android.os.Bundle import android.util.Log import android.widget.Button import androidx.appcompat.app.AppCompatActivity import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val handler = CoroutineExceptionHandler { _, exception -> Log.d("ning", "Caught $exception") } findViewById
package com.dongnaoedu.kotlincoroutineexception import android.util.Log import kotlinx.coroutines.CoroutineExceptionHandler import kotlin.coroutines.CoroutineContext class GlobalCoroutineExceptionHandler : CoroutineExceptionHandler { override val key = CoroutineExceptionHandler override fun handleException(context: CoroutineContext, exception: Throwable) { Log.d("ning","Unhandled Coroutine Exception: $exception")//依然会崩溃 } }
取消与异常紧密相关,协程内部使用CancellationException来进行取消,这个异常会被忽略 当子协程被取消时,不会取消它的父协程 @Test fun `test cancel and exception`() = runBlocking{ val job = launch { val child = launch { try { delay(Long.MAX_VALUE) } finally { println("Child is cancelled.") } } yield() println("Cancelling child") child.cancelAndJoin() yield() println("Parent is not cancelled") } job.join() } 打印输出 Cancelling child Child is cancelled. Parent is not cancelled
//如果一个协程遇到了CancellationException以外的异常,它将使用该异常取消它的父协程。当 //父协程的所有子协程都结束后,异常才会被父协程处理。 @Test fun `test cancel and exception2`() = runBlocking{ val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val job = GlobalScope.launch(handler) { launch { try { delay(Long.MAX_VALUE) } finally { withContext(NonCancellable) { println("Children are cancelled, but exception is not handled until all children terminate") delay(100) println("The first child finished its non cancellable block") } } } launch { delay(10) println("Second child throws an exception") throw ArithmeticException() } } job.join() }
//如果一个协程遇到了CancellationException以外的异常,它将使用该异常取消它的父协程。当 //父协程的所有子协程都结束后,异常才会被父协程处理。 @Test fun `test cancel and exception2`() = runBlocking{ val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception") } val job = GlobalScope.launch(handler) { launch { try { delay(Long.MAX_VALUE) } finally { withContext(NonCancellable) { println("Children are cancelled, but exception is not handled until all children terminate") delay(100) println("The first child finished its non cancellable block") } } } launch { delay(10) println("Second child throws an exception") throw ArithmeticException() } } job.join() } 打印输出: Second child throws an exception Children are cancelled, but exception is not handled until all children terminate The first child finished its non cancellable block Caught java.lang.ArithmeticException
@Test fun `test exception aggregation`() = runBlocking{ val handler = CoroutineExceptionHandler { _, exception -> println("Caught $exception ${exception.suppressed.contentToString()}") } val job = GlobalScope.launch(handler) { launch { try { delay(Long.MAX_VALUE) } finally { throw ArithmeticException() //2 } } launch { try { delay(Long.MAX_VALUE) } finally { throw IndexOutOfBoundsException() //3 } } launch { delay(100) throw IOException() //1 } } job.join() } }
打印输出
Caught java.io.IOException [java.lang.IndexOutOfBoundsException, java.lang.ArithmeticException]
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)