我們把關(guān)于contexts\children和jobs的知識融合到一起,試想我們有一個(gè)具備生命周期的對象,但這個(gè)對象不是一個(gè)協(xié)程。例如,在開發(fā)Android應(yīng)用時(shí),為了執(zhí)行獲取網(wǎng)絡(luò)數(shù)據(jù)、執(zhí)行動(dòng)畫等異步操作,我們在activity的里開啟了各種協(xié)程。為了避免內(nèi)存泄漏,當(dāng)activity被銷毀時(shí),所有的這些協(xié)程必須取消。
通過創(chuàng)建一個(gè)Job的實(shí)例來控制我們協(xié)程的生命周期,把它綁定到activity的生命周期上。在activity被創(chuàng)建時(shí),使用工廠方法Job()創(chuàng)建一個(gè)job實(shí)例;當(dāng)activity被銷毀時(shí),它如下這樣被取消:
class MyActivity : AppCompatActivity(), CoroutineScope {
lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = Dispatchers.Main + job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
job = Job()
}
override fun onDestroy() {
super.onDestroy()
job.cancel() // Cancel job on activity destroy. After destroy all children jobs will be cancelled automatically
}
/*
* Note how coroutine builders are scoped: if activity is destroyed or any of the launched coroutines
* in this method throws an exception, then all nested coroutines are cancelled.
*/
fun loadDataFromUI() = launch { // <- extension on current activity, launched in the main thread
val ioData = async(Dispatchers.IO) { // <- extension on launch scope, launched in IO dispatcher
// blocking I/O operation
}
// do something else concurrently with I/O
val data = ioData.await() // wait for result of I/O
draw(data) // can draw in the main thread
}
}
這個(gè)activity實(shí)現(xiàn)了CoroutineScope接口,我們只需要重寫CoroutineScope.coroutineContext屬性來指定協(xié)程在這個(gè)scope內(nèi)的執(zhí)行上下文環(huán)境context。那么在這個(gè)activity里啟動(dòng)協(xié)程就不需要再明確指定他們的執(zhí)行環(huán)境context。
這里有一段示意代碼
import kotlin.coroutines.*
import kotlinx.coroutines.*
class Activity : CoroutineScope {
lateinit var job: Job
fun create() {
job = Job()
}
fun destroy() {
job.cancel()
}
// to be continued ...
// class Activity continues
override val coroutineContext: CoroutineContext
get() = Dispatchers.Default + job
// to be continued ...
// class Activity continues
fun doSomething() {
// launch ten coroutines for a demo, each working for a different time
repeat(10) { i ->
launch {
delay((i + 1) * 200L) // variable delay 200ms, 400ms, ... etc
println("Coroutine $i is done")
}
}
}
} // class Activity ends
fun main() = runBlocking<Unit> {
val activity = Activity()
activity.create() // create an activity
activity.doSomething() // run test function
println("Launched coroutines")
delay(500L) // delay for half a second
println("Destroying activity!")
activity.destroy() // cancels all coroutines
delay(1000) // visually confirm that they don't work
}
//Launched coroutines
//Coroutine 0 is done
//Coroutine 1 is done
//Destroying activity!
相關(guān)概念
協(xié)程,是線程中的,也就是說一個(gè)線程中可能包含多個(gè)協(xié)程,協(xié)程與協(xié)程之間是可以嵌套的
-
Scope
用來給協(xié)程分組,在同一個(gè)CoroutineScope下創(chuàng)建的協(xié)程(如果沒有顯示指定其他Scope),默認(rèn)都是父子關(guān)系,這樣的好處在于cancel父協(xié)程后,所有的子協(xié)程都可以被一起cancel掉。
-
Context
當(dāng)前協(xié)程的上下文,用于在協(xié)程與協(xié)程之間參數(shù)傳遞。
可以用于聲明當(dāng)前 協(xié)程 在哪一個(gè)線程中聲明,以及當(dāng)前 協(xié)程 被中斷后,在哪一個(gè)線程中恢復(fù)它
-
Job
代表了協(xié)程本身,協(xié)程不僅包含了上下文,其本身還是可執(zhí)行體。
任務(wù),封裝了協(xié)程中需要執(zhí)行的代碼邏輯。Job 可以取消并且有簡單生命周期 -
Dispatcher
調(diào)度器,調(diào)度協(xié)程運(yùn)行在哪個(gè)線程上。決定協(xié)程所在的線程或線程池。它可以指定協(xié)程運(yùn)行于特定的一個(gè)線程、一個(gè)線程池或者不指定任何線程(這樣協(xié)程就會運(yùn)行于當(dāng)前線程)
-
withcontext
不會創(chuàng)建新的協(xié)程,在指定協(xié)程上運(yùn)行掛起代碼塊,并掛起該協(xié)程直至代碼塊運(yùn)行完成