Coroutine 學(xué)習(xí)(一)掛起,CoroutineScope,異常
理解掛起
在我個(gè)人的理解中,掛起、掛起函數(shù)指的是當(dāng)前的函數(shù)可以被「擱置」,不影響主流程的運(yùn)行

在以上的代碼中,launch中的函數(shù)會(huì)立刻執(zhí)行,但是需要等待5s。由于掛起函數(shù)有不影響主流程的特性,所以會(huì)先輸出hello?world,等到5s后再打印the job is done。
但是在這里的代碼中,由于join函數(shù)會(huì)先掛起當(dāng)前的協(xié)程,等到j(luò)ob執(zhí)行完成。當(dāng)前的協(xié)程是什么? 當(dāng)然是由runBlocking引導(dǎo)的協(xié)程,所以會(huì)先打印the job is done.
CoroutineContext
協(xié)程總是運(yùn)行在一些以CoroutineContext類型為代表的上下文中。協(xié)程上下文在類型上更接近一個(gè)List。CoroutineContext包含一個(gè)調(diào)度器

CoroutineScope
CoroutineScope會(huì)跟蹤它使用的launch和aysnc創(chuàng)建的所有協(xié)程。ViewModel有viewModelScope,LifeCycle有l(wèi)ifecycleScope。每一個(gè)協(xié)程構(gòu)建器都是CoroutineScope的擴(kuò)展函數(shù),并且繼承了其中的CoroutineContext。所以當(dāng)使用CoroutineContext的協(xié)程構(gòu)建器創(chuàng)建新的協(xié)程時(shí),會(huì)繼承CoroutineContext中的信息
Job表示協(xié)程的句柄,可以通過這個(gè)類的實(shí)例來管理協(xié)程的生命周期
協(xié)程的異常處理
launch和async的對于異常的處理機(jī)制不同。當(dāng)使用這兩種構(gòu)建器創(chuàng)建根協(xié)程時(shí),launch會(huì)將異常視作未捕獲異常,async需要對異常手動(dòng)處理:
這里的打印行為是通過CoroutineExceptionHandler處理的??梢宰远ǘxCoroutineExceptionHandler:
取消與異常:
在下面的代碼中,父協(xié)程創(chuàng)建的實(shí)例是job,子協(xié)程創(chuàng)建的實(shí)例是child。使用的是Global.launch中調(diào)度的子協(xié)程,所以父子協(xié)程都具有相同的CoroutineContext。yield表示讓出當(dāng)前的協(xié)程調(diào)度器,給使用者同一個(gè)協(xié)程調(diào)度器的另外一個(gè)協(xié)程使用,這個(gè)協(xié)程就是child。但是很明顯,這個(gè)讓給子協(xié)程的動(dòng)作好像只做了一下,如果是Dispatcher一直在子協(xié)程手中,則父協(xié)程不能有機(jī)會(huì)打印后面的內(nèi)容。
job取消主動(dòng)會(huì)拋出異常CancellationException,這個(gè)異常會(huì)被異常handler處理,但是不會(huì)結(jié)束父協(xié)程,所以打印的結(jié)果是:

子協(xié)程的取消不會(huì)影響父協(xié)程的取消,但是如果子協(xié)程遇到了CancellationException之外的異常,異常會(huì)被ExceptionHandler處理,父協(xié)程也隨之結(jié)束:
輸出:
下面的代碼展示了多個(gè)子協(xié)程的情況,如果有其中一個(gè)子協(xié)程拋出異常,父協(xié)程也會(huì)結(jié)束,其他的子協(xié)程也被迫關(guān)閉:
多個(gè)子協(xié)程出現(xiàn)的異常只會(huì)捕捉第一個(gè)。
如果取消父協(xié)程,子協(xié)程也會(huì)被取消嗎?
所以總結(jié)一下父子協(xié)程的關(guān)系:
覆巢之下無完卵: 父協(xié)程取消?子協(xié)程也取消 而且是都要取消
子不孝 父之過: 子協(xié)程取消 父協(xié)程也要取消
所以是一種雙向關(guān)系
SupervisorJob實(shí)現(xiàn)單向關(guān)系
firstChild被取消了,但是并不會(huì)讓secondChild停止,同時(shí)父協(xié)程也沒有被停止。
我們可以使用supervisorScope實(shí)現(xiàn)類似的事情:
類似于SupervisoJob的單項(xiàng)傳播
向coroutineScope一樣,等到所有子協(xié)程結(jié)束之后結(jié)束自身
輸出:
總結(jié):
協(xié)程構(gòu)建器中都有一個(gè)默認(rèn)的CoroutineContext (協(xié)程上下文)
協(xié)程上下文就像List一樣,我們可以往里面添加Dispatcher Job CoroutineExceptionHandler 等等
所有的協(xié)程構(gòu)建器都是CoroutineScope的擴(kuò)展函數(shù),而CoroutineContext則是它的一個(gè)屬性。CoroutineScope指定協(xié)程構(gòu)建器,同時(shí)將自身的CoroutineContext用來配置構(gòu)建器的【上下文信息】(dispatcher exceptionhandler....)
子協(xié)程表示繼承了父協(xié)程CoroutineScope的協(xié)程。普通的CoroutineScope(也是普通的CoroutineContext)中,異常傳遞是雙向的。但是這種雙向傳遞關(guān)系可以被打破,通過在CoroutineContext中設(shè)置SupervisorJob或者直接通過supervisorScope構(gòu)建協(xié)程。