本文基于协程官方文档讲解,具体可查看here
协程上下文包含调度器、异步处理器、拦截器等。当然拦截器一般我们不会用到,它主要是用来进行线程切换的。 launch(xxx){}或者async(xxx){}的第一个参数就是我们的协程上下文。
一、调度器 Dispatchers
public actual object Dispatchers {
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
public val IO: CoroutineDispatcher = DefaultScheduler.IO
}
默认四种调度器,我们可以依据自己的业务选择。
1.1、Dispatchers.Unconfined
fun printMsg(msg:String){
println("${Thread.currentThread().name} "+msg)
}
fun main() = runBlocking { //main线程
val job = launch(Dispatchers.Unconfined) {
printMsg("unconfined before") #1 //main线程
delay(500L) #2 //后台线程
printMsg("unconfined after") #3 //后台线程
}
job.join()
printMsg("Done")
}
子协程体里面的代码执行,可以看成三部分,delay前#1、delay#2、delay后#3,Dispatchers.Unconfined调度器,在子协程启动时,调度的线程是当前线程,这里就是main线程。delay肯定是后台线程执行,delay后#3所在的线程是delay执行完恢复时确定,其线程是delay执行所在的线程。
1.2、自定义调度器(需记得close)
fun main() = runBlocking {
newSingleThreadContext("Ctx1").use { ctx1 ->
newSingleThreadContext("Ctx2").use { ctx2 ->
val job = launch(ctx1) {
printMsg("start in ctx1")
withContext(ctx2) {
printMsg("work in ctx2")
}
printMsg("Back to ctx1")
}
job.join()
}
}
printMsg("Done")
}
这里通过newSingleThreadContext创建了一个调度器,然后
习惯很好的点是它用了use,用完就close了。
二、获取Job
可以通过launch{}函数返回job,也可以使用coroutineContext[Job]来获取。
fun main() = runBlocking {
newSingleThreadContext("Ctx1").use { ctx1 ->
newSingleThreadContext("Ctx2").use { ctx2 ->
val job = launch(ctx1) {
printMsg("start in ctx1 ${coroutineContext[Job]}")
withContext(ctx2) {
printMsg("work in ctx2 ${coroutineContext[Job]}")
}
printMsg("Back to ctx1 ${coroutineContext[Job]}")
}
printMsg("当前子协程job $job")
job.join()
}
}
printMsg("Done")
}
withContext没有新开协程,只是切换了调度器而已。
三、父子协程关系
3.1、默认父协程取消,子协程也会取消。
fun main() = runBlocking {
var request = launch(Dispatchers.Default){
launch {
printMsg("job1:before")
delay(2000)
printMsg("job1:after")
}
launch {
delay(100)
printMsg("job2 before")
delay(1000)
printMsg("job2: after")
}
}
delay(500)
request.cancel()
delay(1000)
printMsg("who can survived")
}
父协程取消了,子协程都响应取消了。我们可以捕捉下取消时的异常。
fun main() = runBlocking {
var request = launch(Dispatchers.Default) {
launch {
try {
printMsg("job1:before")
delay(2000)
printMsg("job1:after")
} catch (e: Exception) {
printMsg("job1 $e")
}
}
launch {
try {
delay(100)
printMsg("job2: before")
delay(1000)
printMsg("job2: after")
} catch (e: Exception) {
printMsg("job2 $e")
}
}
}
delay(500)
request.cancel()
delay(1000)
printMsg("who can survived")
}
父协程取消,子协程都能捕捉到这个取消异常。
3.2、父协程取消,子协程不取消的场景
3.2.1 GlobalScope.launch开启一个独立的协程
GlobalScope.launch{xxx} ,严格来讲它不是外部协程的子协程,它开辟了一个独立的协程,其生命周期也跟外层协程无关。
fun main() = runBlocking {
var job1: Job? = null
var request = launch(Dispatchers.Default) {
job1 = GlobalScope.launch { //开启一个独立的协程了
try {
printMsg("job1:before")
delay(2000)
printMsg("job1:after")
} catch (e: Exception) {
printMsg("job1 $e")
}
}
launch {
try {
delay(100)
printMsg("job2: before")
delay(1000)
printMsg("job2: after")
} catch (e: Exception) {
printMsg("job2 $e")
}
}
}
delay(500)
request.cancel()
job1?.join()
delay(1000)
printMsg("who can survived")
}
很明显没有响应外部协程的取消,job1还是完成了自己的任务。
3.2.2 添加新的Job对象
fun main() = runBlocking {
var job1: Job? = null
var job2: Job? = null
var request = launch(Dispatchers.Default) {
job1 = launch(Job()) { //这里添加了Job()
try {
printMsg("job1:before")
delay(2000)
printMsg("job1:after")
} catch (e: Exception) {
printMsg("job1 $e")
}
}
launch {
try {
delay(100)
printMsg("job2: before")
delay(1000)
printMsg("job2: after")
} catch (e: Exception) {
printMsg("job2 $e")
}
}
}
delay(500)
request.cancel()
job1?.join()
delay(1000)
printMsg("who can survived")
}
四、自定义协程名CoroutineName
fun main() = runBlocking {
var request = launch(Dispatchers.Default) {
launch(CoroutineName("V11协程")) {
printMsg("job1:before")
delay(2000)
printMsg("job1:after")
}
launch(CoroutineName("V22协程")) {
delay(100)
printMsg("job2: before")
delay(1000)
printMsg("job2: after")
}
}
request.join()
printMsg("who can survived")
}
五、合并上下文 直接相加+
fun main() = runBlocking {
launch(CoroutineName("V1coroutine") + Dispatchers.Default) { //上下文直接相加
printMsg("job1:I run in my own job and execute independently")
delay(3000)
printMsg("job1:I am not affected by cancellation ")
}.join()
printMsg("Done")
}
六、android的MainScope使用
class MainActivity : AppCompatActivity() {
private var mainScope = MainScope()
override fun onDestroy() {
super.onDestroy()
mainScope.cancel()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainScope.launch {
delay(1000)
}
}
}
七、Thread_local Data
val threadLocal = ThreadLocal<String>()
fun main() = runBlocking {
threadLocal.set("aaaaa")
launch(CoroutineName("V1coroutine") + Dispatchers.Default +
threadLocal.asContextElement(value = "bbbbb")) {
printMsg("V1coroutine thread ${threadLocal.get()}")
delay(3000)
printMsg("V1coroutine thread ${threadLocal.get()}")
}
delay(500)
printMsg("current thread ${threadLocal.get()}")
}