導(dǎo)入庫
api 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.1.0'
api 'org.jetbrains.kotlinx:kotlinx-coroutines-core-common:1.1.0'
隨筆
GlobalScope
GlobalScope為是全局的尸执,生命周期是app的生命周期如失。
全局協(xié)程類似于守護(hù)線程.在 [GlobalScope]作用范圍內(nèi)啟動的活躍的協(xié)程,
不會保持應(yīng)用程序的整個進(jìn)程存活. 它們的行為就象守護(hù)線程一樣.不會蓖使螅活進(jìn)程.
GlobalScope.launch 時, 我們創(chuàng)建了一個頂級的協(xié)程. 雖然它是輕量的, 但它運(yùn)行時還是會消耗一些內(nèi)存資源.
由GlobalScope啟動的協(xié)程
( GlobalScope.launch { }/GlobalScope.async { })
任務(wù)總是在子線程執(zhí)行
runBlocking
runBlocking { }
任務(wù)執(zhí)行在當(dāng)前啟動線程抗俄,并且會阻塞當(dāng)前線程
在主線程啟動則任務(wù)在主線程執(zhí)行动雹,在子線程啟動任務(wù)則在子線程執(zhí)行
Job
job.cancel() // 取消 job
job.join() // 等待 job 結(jié)束
Job.cancelAndJoin()//取消作業(yè)并暫停調(diào)用協(xié)同程序胰蝠,直到取消的作業(yè)完成
由于有些協(xié)程不會等待子協(xié)程執(zhí)行,所以可以調(diào)用子協(xié)程的join方法躲庄,
告訴父協(xié)程噪窘,請等待我執(zhí)行完畢效扫,例如:
fun main() = runBlocking {
val job = GlobalScope.launch {
// 啟動新的協(xié)程, 并保存它的執(zhí)行任務(wù)的引用
//由于是全局協(xié)程荡短,所以runBlocking 不會等待他執(zhí)行完成
delay(1000L)
println("World!")
}
println("Hello,")
job.join() // 等待, 直到子協(xié)程執(zhí)行完畢
}
fun main() =runBlocking {
launch{//由于屬于子協(xié)程掘托,不需要手動join闪盔,runBlocking 會等待他完成
delay(2000L)
println("World!") // 后打印
}
println("Hello,")//先打印,打印完成仍會等待launch執(zhí)行
}
//嵌套coroutineScope/runBlocking,會自動等待子協(xié)程執(zhí)行完成听绳,再執(zhí)行后面的
fun main() =runBlocking {
coroutineScope {//新的作用域椅挣,注意區(qū)分launch,runBlocking 會等待它執(zhí)行完成再執(zhí)行后面的代碼
delay(200L)
println("Hello,")//等待之后鼠证,先打印
}
println("World!") // 后打印量九,等待launch執(zhí)行完再執(zhí)行
}
coroutineScope
[runBlocking] 和 [coroutineScope] 之間的主要區(qū)別是,
[coroutineScope]在等待子協(xié)程運(yùn)行時, 不會阻塞當(dāng)前線程
coroutineScope {
// 創(chuàng)建新的協(xié)程作用范圍
launch {
delay(500L)
println("World!")//后打印
}
println("Hello,") // 先打印,打印完等待launch執(zhí)行完成之后才結(jié)束
}
launch和async
launch和async區(qū)別
launch里面的代碼會立即執(zhí)行
async的代碼需要手動執(zhí)行(使用await())
suspend
專門修飾協(xié)程方法荠列,例如
fun main() = runBlocking {
launch { doWorld() }
println("Hello,")
}
// 這是你的第一個掛起函數(shù)
suspend fun doWorld() {
delay(1000L)
println("World!")
}
但是如果抽取出來的函數(shù)包含一個協(xié)程構(gòu)建器, 并且這個構(gòu)建器需要在當(dāng)前作用范圍上調(diào)用, 那么怎么辦?
這種情況下, 對于被抽取出來的函數(shù)來說只有 `suspend` 修飾符是不夠的.
有一種解決辦法是把 `doWorld` 變成 `CoroutineScope` 的擴(kuò)展函數(shù), 但這種辦法有時候并不適用,
因為它會使得 API 難于理解. 理想的解決辦法是, 要么明確地把 `CoroutineScope` 作為一個類的域變量,
再讓這個類包含我們抽取的函數(shù), 或者讓外層類實現(xiàn) `CoroutineScope` 接口, 于是就可以隱含的實現(xiàn)這個目的.
最后一種辦法就是, 可以使用 [CoroutineScope(coroutineContext)],
但這種方法從結(jié)構(gòu)上來說并不安全, 因為你不再能夠控制當(dāng)前方法運(yùn)行時所屬的作用范圍.
只有私有 API 才能夠使用這個構(gòu)建器.
協(xié)程的取消
協(xié)程的取消是 *協(xié)作式的*. 協(xié)程的代碼必須與外接配合, 才能夠被取消.
`kotlinx.coroutines` 庫中的所有掛起函數(shù)都是 *可取消的*.
這些函數(shù)會檢查協(xié)程是否被取消, 并在被取消時出 [CancellationException]異常.
但是, 如果一個協(xié)程正在進(jìn)行計算, 并且沒有檢查取消狀態(tài), 那么它是不可被取消的
//可以取消(唯一的不同就是判斷條件)
runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
repeat (1000) { // 一個執(zhí)行計算的循環(huán)戚宦,只是為了占用 CPU
// 每秒打印消息兩次
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // 等待一段時間
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消一個作業(yè)并且等待它結(jié)束
println("main: Now I can quit.")
}
//無法取消(唯一的不同就是判斷條件)
runBlocking {
val startTime = System.currentTimeMillis()
val job = launch(Dispatchers.Default) {
var nextPrintTime = startTime
var i = 0
while (i<1000) { // 一個執(zhí)行計算的循環(huán)受楼,只是為了占用 CPU
// 每秒打印消息兩次
if (System.currentTimeMillis() >= nextPrintTime) {
println("job: I'm sleeping ${i++} ...")
nextPrintTime += 500L
}
}
}
delay(1300L) // 等待一段時間
println("main: I'm tired of waiting!")
job.cancelAndJoin() // 取消一個作業(yè)并且等待它結(jié)束
println("main: Now I can quit.")
}
withContext
可以使用 [withContext] 函數(shù)和 [NonCancellable] 上下文,
把相應(yīng)的代碼包裝在 `withContext(NonCancellable) {...}`
withTimeout
//到達(dá)超時后自動取消
fun main() = runBlocking {
//sampleStart
withTimeout(1300L) {
repeat(1000) { i ->
println("I'm sleeping $i ...")
delay(500L)
}
}
//sampleEnd
}
withTimeoutOrNull
使用 [withTimeoutOrNull]函數(shù), 它與 [withTimeout] 函數(shù)類似,
但在超時發(fā)生時, 它會返回 `null`, 而不是拋出異常:
async
在概念上,[async]就類似于 [launch]河狐。
它啟動了一個單獨的協(xié)程,這是一個輕量級的線程并與其它所有的協(xié)程一起并發(fā)的工作瑟捣。
不同之處在于 `launch` 返回一個 [Job] 并且不附帶任何結(jié)果值馋艺,
而 `async` 返回一個 [Deferred] —— 一個輕量級的非阻塞 future, 這代表了一個將會在稍后提供結(jié)果的 promise迈套。
你可以使用 `.await()` 在一個延期的值上得到它的最終結(jié)果捐祠, 但是 `Deferred` 也是一個 `Job`,
所以如果需要的話桑李,你可以取消它踱蛀。
val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
使用了CoroutineStart.LAZY需要手動調(diào)用start(),然后再使用await()
協(xié)程調(diào)度器
Unconfined : I'm working in thread main
Default : I'm working in thread DefaultDispatcher-worker-1
newSingleThreadContext: I'm working in thread MyOwnThread
main runBlocking : I'm working in thread main
當(dāng)調(diào)用 `launch { …… }` 時不傳參數(shù),它從啟動了它的 [CoroutineScope] 中承襲了上下文(以及調(diào)度器)贵白。
在這個案例中率拒,它從 `main` 線程中的 `runBlocking` 主協(xié)程承襲了上下文。
[Dispatchers.Unconfined]是一個特殊的調(diào)度器且似乎也運(yùn)行在 `main` 線程中禁荒,但實際上猬膨, 它是一種不同的機(jī)制勃痴,這會在后文中講到。
該默認(rèn)調(diào)度器,當(dāng)協(xié)程在 [GlobalScope]中啟動的時候使用衫贬, 它代表 [Dispatchers.Default]使用了共享的后臺線程池
所以 `GlobalScope.launch { …… }` 也可以使用相同的調(diào)度器—— `launch(Dispatchers.Default) { …… }`
[newSingleThreadContext]為協(xié)程的運(yùn)行啟動了一個線程。 一個專用的線程是一種非常昂貴的資源葬毫。
在真實的應(yīng)用程序中兩者都必須被釋放,當(dāng)不再需要的時候,使用 [close] 函數(shù)汛骂,或存儲在一個頂層變量中使它在整個應(yīng)用程序中被重用
CoroutineScope
創(chuàng)建一個新的作用域
重點,在安卓中使用這個創(chuàng)建的才不會阻塞主線程,用runBlock會阻塞主線
CoroutineScope(context: CoroutineContext): CoroutineScope
例如:CoroutineScope(Dispatchers.Main)