《Android Kotlin協(xié)程實(shí)戰(zhàn)》
https://www.bilibili.com/video/BV1uo4y1y7ZF/?spm_id_from=333.999.0.0&vd_source=b5ef87e2f0a873011363576f8bdcba61
動(dòng)腦學(xué)院 你能聽(tīng)懂的Kotlin協(xié)程課潘酗,跟老司機(jī)學(xué)停蕉,不用自己瞎折騰
###1.認(rèn)識(shí)協(xié)程
協(xié)程是什么津函?協(xié)程基于線(xiàn)程,它是輕量級(jí)線(xiàn)程
異步任務(wù)和協(xié)程對(duì)比缝裁? 協(xié)程讓異步邏輯同步化枢贿,杜絕回調(diào)地獄瑰抵,協(xié)程的核心是函數(shù)或者一段程序能夠被掛起暇榴,稍后再在掛起的位置恢復(fù)
協(xié)程的依賴(lài): kotlinx-coroutines-core, kotlinx-coroutines-android
協(xié)程的掛起與恢復(fù): suspend: 暫停執(zhí)行當(dāng)前協(xié)程,并保存所有的局部變量尤仍; resume:讓已暫停的協(xié)程從其暫停處繼續(xù)執(zhí)行
掛起函數(shù):suspend關(guān)鍵字修飾箫津,只能在協(xié)程體內(nèi)或其他掛起函數(shù)內(nèi)調(diào)用
掛起和阻塞的區(qū)別? 主線(xiàn)程遇到掛起(如協(xié)程里調(diào)用了delay)宰啦,先記下掛起點(diǎn)苏遥,然后該干嘛干嘛压语,如刷新UI学辱,等掛起結(jié)束了您访,繼續(xù)執(zhí)行下面呻逆;
主線(xiàn)程如果阻塞了(如Thread.sleep),就啥事不干驰吓,一直等結(jié)束(阻塞就是線(xiàn)程被占用了提完,不能干其他事情)
Choreographer: Skipped xxx frames! The application may be doing too much work on its main thead.
協(xié)程兩部分:基礎(chǔ)設(shè)施層和業(yè)務(wù)框架層
基礎(chǔ)設(shè)施層代碼實(shí)例: val continuation=suspend {5}.createCoroutine(object : Continuation<Int> {override fun resumeWith(result: Result<Int>) {}}); continuation.resume(Unit)
協(xié)程調(diào)度器:
Dispatchers.Main : Android上的主線(xiàn)程
Dispatcher.IO: 非主線(xiàn)程
Dispatcher.Default 非主線(xiàn)程宋列,專(zhuān)為CPU密集型任務(wù)進(jìn)行了優(yōu)化
協(xié)程作用域:
CoroutineScope: 會(huì)跟蹤所有協(xié)程辆布,同樣可以取消它所啟動(dòng)的所有協(xié)程
GlobalScope: 生命周期是process級(jí)別的瞬矩,即使Activity或Fragment已經(jīng)被銷(xiāo)毀,協(xié)程仍然在執(zhí)行
MainScope:在Activity中使用锋玲,可以在onDestory中取消協(xié)程景用,cancel()方法,取消會(huì)報(bào)異常JobCancellationException: Job was cancelled;
viewModelScope: 只能在ViewModel中使用惭蹂,綁定ViewModel的生命周期
lifecycleScope:只能在Activity或Fragment中使用伞插,會(huì)綁定Activity和Fragment的生命周期
###2.協(xié)程的啟動(dòng)與取消
協(xié)程構(gòu)建器:
launch: 返回一個(gè)Job并且不附帶任何結(jié)果
async:返回一個(gè)Deferred割粮,Deferred也是一個(gè)Job,可以使用await在一個(gè)延期的值上得到最終的結(jié)果
等待上一個(gè)任務(wù)執(zhí)行完:launch+join(); async+await()
runBlocking {} 讓我們的主線(xiàn)程變成一個(gè)協(xié)程
協(xié)程的啟動(dòng)模式:
CoroutineStart.DEFAULT: 協(xié)程創(chuàng)建后蜂怎,立即開(kāi)始調(diào)度穆刻,在調(diào)度前協(xié)程如果被取消,其將直接進(jìn)入取消響應(yīng)的狀態(tài)
CoroutineStart.ATOMIC: 協(xié)程創(chuàng)建后杠步,立即開(kāi)始調(diào)度,協(xié)程執(zhí)行到第一個(gè)掛起點(diǎn)之前不響應(yīng)取消
CoroutineStart.LAZY: 只有協(xié)程被需要時(shí)榜轿,包括主動(dòng)調(diào)用協(xié)程的start幽歼,join或者await函數(shù)時(shí)才會(huì)開(kāi)始調(diào)度,如果調(diào)度前就被取消谬盐,那么該協(xié)程將直接進(jìn)入異常結(jié)束狀態(tài)
CoroutineStart.UNDISPATCHED: 協(xié)程創(chuàng)建后立即在當(dāng)前函數(shù)調(diào)用棧中執(zhí)行甸私,直到遇到第一個(gè)真正掛起的點(diǎn)
如何指定協(xié)程上下文是Dispatchers.IO, 但是協(xié)程任務(wù)的執(zhí)行仍然還在主線(xiàn)程? 執(zhí)行協(xié)程啟動(dòng)模式為CoroutineStart.UNDISPATCHED
協(xié)程作用域構(gòu)建器:
coroutineScope與runBlocking
runBlocking是常規(guī)函數(shù)飞傀,coroutineScope是掛起函數(shù)
Job對(duì)象
對(duì)于每一個(gè)創(chuàng)建的協(xié)程(通過(guò)launch或者async)皇型,會(huì)返回一個(gè)Job實(shí)例,負(fù)責(zé)管理協(xié)程的生命周期
新創(chuàng)建New砸烦,活躍Active弃鸦, 完成中Completing,已完成Completed幢痘,取消中Cancelling唬格,已取消Cancelled,可以訪問(wèn)Job的屬性:isActive颜说,isCancelled和isCompleted
取消協(xié)程:
取消作用域會(huì)取消它的子協(xié)程购岗;被取消的子協(xié)程不會(huì)影響其余兄弟協(xié)程;協(xié)程通過(guò)拋出一個(gè)特殊的異常CancellationException來(lái)處理取消操作
CPU密集型任務(wù)取消
isActive檢查Job是否處于活躍狀態(tài)门粪;ensureActive(),如果job處于非活躍狀態(tài)會(huì)立即拋出異常喊积;yield函數(shù)如果取消會(huì)拋出CancellationException異常,還會(huì)嘗試出讓線(xiàn)程的執(zhí)行權(quán)
use函數(shù):只能被實(shí)現(xiàn)了Closeable的對(duì)象使用玄妈,程序結(jié)束的時(shí)候會(huì)自動(dòng)調(diào)用close方法乾吻,適合文件對(duì)象
不想?yún)f(xié)程被取消: withContext(NonCancellable) {}
超時(shí)任務(wù):withTimeout(1000) {}; withTimeoutOrNull(1000) {}
###3.協(xié)程的異常處理
協(xié)程上下文CoroutineContext是一組定義協(xié)程行為的元素
Job:控制協(xié)程的生命周期
CoroutineDispatcher: 向合適的線(xiàn)程分發(fā)任務(wù)
CoroutineName: 協(xié)程的名稱(chēng)
CoroutineExceptionHandler: 處理未被捕獲的異常
組合協(xié)程上下文: launch(Dispatchers.Default + CoroutineName("test")) {} --重載了加號(hào)運(yùn)算符
協(xié)程上下文的繼承:CoroutineScope協(xié)程作用域-》launch協(xié)程-》async子協(xié)程
新創(chuàng)建的協(xié)程,它的CoroutineContext會(huì)包含一個(gè)全新的Job實(shí)例措近,剩下的元素從CoroutineContext的父類(lèi)繼承溶弟,該父類(lèi)可能是另外一個(gè)協(xié)程或者創(chuàng)建該協(xié)程的CoroutineScope
根協(xié)程異常傳播:launch里拋出的異常:可以在launch協(xié)程里捕捉;async里拋出的異常瞭郑,調(diào)用await后才能捕捉
非根協(xié)程的異常:其他協(xié)程創(chuàng)建的協(xié)程中辜御,產(chǎn)生的異常總是會(huì)被傳播
異常的傳播特性:當(dāng)一個(gè)協(xié)程由于異常運(yùn)行失敗時(shí)屈张,它會(huì)傳播這個(gè)異常并傳遞給它的父級(jí)擒权,接下來(lái)父級(jí)會(huì)取消它自己的子級(jí)袱巨,取消它自己,將異常傳播并傳遞給它的父級(jí)
SupervisorJob: 一個(gè)子協(xié)程的運(yùn)行失敗不會(huì)影響到其他子協(xié)程碳抄,SupervisorJob不會(huì)傳播異常給它的父級(jí)愉老,它會(huì)讓子協(xié)程自己處理異常
CoroutineScope(SupervisorJob())
作用域構(gòu)建器supervisorScope {}, 在supervisorScope里創(chuàng)建的子協(xié)程里拋出異常,不會(huì)影響其他子協(xié)程剖效;如果在supervisorScope作用域里拋出異常嫉入,所有子作業(yè)將會(huì)被全部取消
異常的捕獲:使用CoroutineExceptionHandler對(duì)協(xié)程的異常進(jìn)行捕獲
取消與異常:取消(如joinAndCancel)會(huì)拋出CancellationException異常,當(dāng)子協(xié)程被取消時(shí)璧尸,不會(huì)取消它的父協(xié)程
異常聚合:CoroutineExceptionHandler { _, exception-> ${exception.suppressed.contentToString() --打印第二個(gè)異常}}
###4.Flow-異步流
異步返回多個(gè)值: flow<Int> { }咒林,
flow: Flow構(gòu)建器函數(shù); flow{...} 構(gòu)建快中代碼可以?huà)炱穑?流使用emit函數(shù)發(fā)送值爷光; 使用collect函數(shù)收集值
Flow應(yīng)用: Background Thread emit數(shù)據(jù) -》Main Thread 去collect數(shù)據(jù)
冷流: flow構(gòu)建器中的代碼直到流被收集的時(shí)候才運(yùn)行
流構(gòu)建器:
flowOf()發(fā)射固定值的流
使用asFlow擴(kuò)展函數(shù)垫竞,可以將各種集合與序列轉(zhuǎn)換成流: (1..3).asFlow().collect { value -> println(value) }
流的上下文:flowOn操作符:用于更改流發(fā)射的上下文; launchIn指定在那個(gè)協(xié)程里收集數(shù)據(jù)
流的取消: withTimeoutOrNull(2500) {}
流的取消檢測(cè): 流構(gòu)建器對(duì)每個(gè)發(fā)射值執(zhí)行附加的ensureActive檢測(cè)以進(jìn)行取消
如果想流可以被取消蛀序,需要明確指定cancellable: (1..5).asFlow().cancellable().collect { value -> if (value == 3) cancel() }
背壓: 生產(chǎn)者生產(chǎn)效率大于消費(fèi)者消費(fèi)效率
buffer() 并發(fā)運(yùn)行流中發(fā)射元素的代碼
conflate() 合并發(fā)射項(xiàng)欢瞪; collectLatest()
操作符: 轉(zhuǎn)換操作符: map, transform; 限長(zhǎng)操作符:take;
末端操作符: reduce ; 1到5平方并累加 (1..5).asFlow().map{ it*it }.reduce{ a,b->a+b }
組合多個(gè)流: zip
展平流: flatMapConcat連接模式, flatMapMerge合并模式, flatMapLatest最新展平模式
流的異常: 收集的時(shí)候處理異常: 如check(value <= 1) {} ; 發(fā)射的時(shí)候拋出異常徐裸,使用catch函數(shù)捕捉
流的完成: onCompletion