This article is based on the official documentation of coroutines, which can be found here
The coroutine context contains schedulers, asynchronous processors, interceptors, and so on. Interceptors, of course, are not usually used; they are mainly used for thread switching. The first argument to launch(XXX){} or async(XXX){} is our coroutine context.
First, 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
}
Copy the code
There are four default schedulers, which we can choose according to our own business.
1.1, Dispatchers. Unconfined
fun printMsg(msg:String){
println("${Thread.currentThread().name} "+msg)
}
fun main(a) = runBlocking { / / main thread
val job = launch(Dispatchers.Unconfined) {
printMsg("unconfined before") #1 / / main thread
delay(500L) #2 // background threads
printMsg("unconfined after") #3 // background threads
}
job.join()
printMsg("Done")}Copy the code
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Delay must be executed by the background thread, and the thread of #3 after delay is determined when delay is restored, and its thread is the thread of delay execution.
1.2. Customize the scheduler (remember close)
fun main(a) = 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")}Copy the code
Here we create a scheduler with newSingleThreadContext, and thenA good habit is that it uses the use of close
.
2. Get jobs
The job can be returned using the launch{} function or retrieved using coroutineContext[job].
fun main(a) = 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("Current subcoroutine Job$job")
job.join()
}
}
printMsg("Done")}Copy the code
WithContext doesn’t have a new coroutine, it just switches the scheduler.
3. Parent-child coroutine relationship
3.1. The default parent coroutine is cancelled, and the child coroutine is also cancelled.
fun main(a) = 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")}Copy the code
The parent coroutine is canceled, the child coroutine is canceled. We can catch the exception on cancellation.
fun main(a) = 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")}Copy the code
The parent coroutine cancels and the child coroutine catches the cancellation exception.
3.2 Scenario where the parent coroutine cancels and the child coroutine does not cancel
3.2.1 GlobalScope.launch Launches a separate coroutine
Globalscope. launch{XXX}, which is not strictly a child of an external coroutine, opens a separate coroutine with a lifetime independent of the outer coroutine.
fun main(a) = runBlocking {
var job1: Job? = null
var request = launch(Dispatchers.Default) {
job1 = GlobalScope.launch { // start a separate coroutine
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")}Copy the code
Apparently job1 did its job without responding to the cancellation of the external coroutine.
3.2.2 Adding a Job Object
fun main(a) = 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")}Copy the code
4. Custom CoroutineName CoroutineName
fun main(a) = runBlocking {
var request = launch(Dispatchers.Default) {
launch(CoroutineName("V11 coroutines." ")) {
printMsg("job1:before")
delay(2000)
printMsg("job1:after")
}
launch(CoroutineName("V22 coroutines." ")) {
delay(100)
printMsg("job2: before")
delay(1000)
printMsg("job2: after")
}
}
request.join()
printMsg("who can survived")}Copy the code
Add + to add context
fun main(a) = runBlocking {
launch(CoroutineName("V1coroutine") + Dispatchers.Default) { // Add context directly
printMsg("job1:I run in my own job and execute independently")
delay(3000)
printMsg("job1:I am not affected by cancellation ")
}.join()
printMsg("Done")}Copy the code
Six, android MainScope use
class MainActivity : AppCompatActivity() {
private var mainScope = MainScope()
override fun onDestroy(a) {
super.onDestroy()
mainScope.cancel()
}
override fun onCreate(savedInstanceState: Bundle?). {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainScope.launch {
delay(1000)}}}Copy the code
Seven, Thread_local Data
val threadLocal = ThreadLocal<String>()
fun main(a) = 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()}")}Copy the code