2021-10-31王学岗Kotlin协程(三)

2021-10-31王学岗Kotlin协程(三),第1张

2021-10-31王学岗Kotlin协程(三) 协程的上下文

我们使用构建器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]

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/4999361.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-11-14
下一篇 2022-11-14

发表评论

登录后才能评论

评论列表(0条)

保存