overview
Kotlin 協(xié)程的基本概念:協(xié)程是靠編譯器實(shí)現(xiàn)的,并不需要操作系統(tǒng)和硬件的支持。
CoroutineContext 協(xié)程上下文,它包含一個(gè)默認(rèn)的協(xié)程調(diào)度器,所有協(xié)程都必須在CoroutineContext中執(zhí)行。
CoroutineScope 協(xié)程作用域,它一個(gè)接口只包含一個(gè)屬性CoroutineContext,它定義了一個(gè)有生命周期的實(shí)體上的實(shí)現(xiàn)。
GlobalScope 一個(gè)全局的作用域,用于啟動(dòng)頂級(jí)的協(xié)程,這些協(xié)程會(huì)在整個(gè)應(yīng)用程序的生命周期內(nèi)運(yùn)行。
CoroutineDispatcher 協(xié)程調(diào)度器,它用來調(diào)度和處理任務(wù),決定了協(xié)程應(yīng)該在哪個(gè)或哪些線程中執(zhí)行。
lifecycleScope:生命周期范圍,用于activity等有生命周期的組件,在DESTROYED的時(shí)候會(huì)自動(dòng)結(jié)束。
viewModelScope:viewModel范圍,用于ViewModel中,在ViewModel被回收時(shí)會(huì)自動(dòng)結(jié)束。
CoroutineScope的作用域類型:
頂級(jí)作用域:沒有父協(xié)程的協(xié)程所在的作用域?yàn)轫敿?jí)作用域。
協(xié)同作用域:協(xié)程中啟動(dòng)新的協(xié)程,新協(xié)程為所在協(xié)程的子協(xié)程,這種情況下子協(xié)程拋出的未捕獲異常將傳遞給父協(xié)程處理,父協(xié)程同時(shí)也會(huì)被取消。
主從作用域:與協(xié)同作用域類似,但子協(xié)程的異常不會(huì)向上傳遞給父協(xié)程。
CoroutineScope的聲明方式:(它們都遵循CoroutineScope interface,所以你可以自定義一個(gè)協(xié)程)
GlobalScope.launch:聲明頂級(jí)作用域。
GlobalScope.async:聲明頂級(jí)作用域。
runBlocking:聲明頂級(jí)作用域。
coroutineScope:聲明協(xié)同作用域。
supervisorScope:聲明主從作用域,異常不會(huì)向上傳遞。
Kotlin中包含四種協(xié)程調(diào)度器,它們的作用分別是:
Dispatchers.Default:這是默認(rèn)調(diào)度器,適合處理CPU密集型任務(wù),如大量計(jì)算。它是一個(gè)CPU密集型任務(wù)調(diào)度器,通常用于后臺(tái)計(jì)算。
Dispatchers.IO:這是IO調(diào)度器,適合執(zhí)行IO相關(guān)操作,如文件讀寫、網(wǎng)絡(luò)請(qǐng)求等。它是一個(gè)IO密集型任務(wù)調(diào)度器,通常用于處理文件操作和網(wǎng)絡(luò)IO操作。
Dispatchers.Main:這是UI調(diào)度器,通常在主線程上執(zhí)行任務(wù),如更新UI。在Android平臺(tái)上,這通常用于處理UI相關(guān)的操作。
Dispatchers.Unconfined:這是非受限制調(diào)度器,不會(huì)要求協(xié)程在哪個(gè)線程上執(zhí)行。它會(huì)在啟動(dòng)它的線程上執(zhí)行,直到第一個(gè)掛起點(diǎn),然后恢復(fù)執(zhí)行。
協(xié)程調(diào)度器的使用場(chǎng)景和特點(diǎn):
- Dispatchers.Default:適用于需要大量計(jì)算的任務(wù),如數(shù)據(jù)排序、解析等。
- Dispatchers.IO:適用于文件讀寫、數(shù)據(jù)庫(kù)操作和網(wǎng)絡(luò)請(qǐng)求等IO密集型任務(wù)。
- Dispatchers.Main:適用于需要在主線程上執(zhí)行的UI更新操作
... 開始代碼示例:將會(huì)涉及到幾乎所有的協(xié)程
- launch && async
val job1 = GlobalScope.launch(start = CoroutineStart.LAZY) {
delay(1000)
println("hello launch")
// async
val result1 = async(start = CoroutineStart.DEFAULT) {
delay(1000)
println("hello async")
}
// invakeOnCompletion
result1.invokeOnCompletion {
if (it != null) {
// result1.cancelAndJoin() 添加之后執(zhí)行這里
println("exception: ${it.message}")
}
else {
println("result is complete")
}
}
result1.cancelAndJoin()
}
// 默認(rèn)是 CoroutineStart.DEFAULT,不需要調(diào)用job1.start()
// CoroutineStart.LAZY時(shí)需要手動(dòng)調(diào)用job1.start(),相當(dāng)于懶加載
// 使用 start 方法來啟動(dòng)協(xié)程,不等待協(xié)程完成
val what1 = job1.start()
- runBlocking
val what2 = runBlocking {
delay(1000)
println("hello runBlocking")
launch {
delay(1000)
println("hello launch")
}
}
- runBlocking + yield
yield 用于掛起當(dāng)前協(xié)程,將當(dāng)前協(xié)程分發(fā)到CoroutineDispatcher隊(duì)列,等其他協(xié)程完成/掛起之后,在繼續(xù)執(zhí)行先前的協(xié)程。
val what3 = runBlocking {
val job1 = launch {
println('1')
yield()
println('3')
yield()
println('5')
}
val job2 = launch {
println('2')
yield()
println('4')
yield()
println('6')
}
println('0')
// 無論是否調(diào)用以下兩句,上面兩個(gè)協(xié)程都會(huì)執(zhí)行
job1.join()
job2.join()
// 執(zhí)行結(jié)果為:0 1 2 3 4 5 6
}
- withContext + CoroutineScope
val what4 = CoroutineScope(context = Dispatchers.Default).launch {
val result1 = withContext(this.coroutineContext){
delay(2000)
1
}
val result2 = withContext(Dispatchers.IO) {
delay(1000)
2
}
val result3 = result1 + result2
println("${result3}")
}
- withContext + CoroutineScope + NonCancellable
val what5 = CoroutineScope(Dispatchers.Default).launch {
withContext(NonCancellable){
delay(2000)
println("this code can not cancel")
}
}
// 取消也沒用,what5會(huì)正常執(zhí)行
val CanNot = what5.cancel()
- CoroutineScope + coroutineScope
val what6 = CoroutineScope(Dispatchers.Default).launch {
val result1 = withContext(this.coroutineContext){
delay(2000)
1
}
// coroutineScope
val result2 = coroutineScope {
delay(2000)
2
}
val result3 = result1 + result2
println("${result3}")
}
- CoroutineScope + jobs
var jobs = ArrayList<Job>()
var isAdd = false
val what7 = CoroutineScope(Dispatchers.Default).launch {
val job1 = this.launch(context = Dispatchers.Unconfined) {
println("Unconfined : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job1)
val job2 = this.launch (context = this.coroutineContext){
// 使用父級(jí)上下文,也就是runBlocking的上下文
println("coroutineContext : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job2)
val job3 = this.launch (context = Dispatchers.Default){
// 使用父級(jí)上下文,也就是runBlocking的上下文
println("'Dispatchers.Default' : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job3)
val job4 = this.launch {
println("'default' : I am is working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job4)
val job5 = this.launch(context = newSingleThreadContext("ThreadForHong")) {
println("'TreadForHong': I am working in thread ${Thread.currentThread()}")
}
isAdd = jobs.add(job5)
if(isAdd) {
jobs.forEach {
it.join()
}
}
// 執(zhí)行結(jié)果如下:(每次執(zhí)行順序不同)
/*
* Unconfined : I am is working in thread Main
* 'Dispatchers.Default' : I am is working in thread DefaultDispatcher-worker-1
* 'TreadForHong': I am working in thread ThreadForHong
* coroutineContext : I am is working in thread Main
* 'default' : I am is working in thread Main
* */
}
- 父子協(xié)程
val what8 = GlobalScope.launch {
// 第一個(gè)使用不同的上下文
val job1 = GlobalScope.launch {
println("job1")
delay(1000)
println("job1 context ...")
}
// 不用寫this.coroutineContext,默認(rèn)也是 this.coroutineContext
val job2 = this.launch(context = this.coroutineContext) {
println("job2")
delay(1000)
println("job2 context ...")
}
job1.join()
job2.join()
}
val what8in = what8.cancel()
// 執(zhí)行結(jié)果:
/*
* job1
* job2
* job1 context ...
*
* job2 context ...它沒有被打印,原因:父級(jí)Job取消執(zhí)行,what8.cancel()
* 如果job2 context = Dispatchers.Default,則不會(huì)跟隨父級(jí)Job取消而取消,依然執(zhí)行
* */
- 父子協(xié)程完善執(zhí)行代碼:
val what9 = runBlocking {
val job1 = this.launch(context = this.coroutineContext) {
println("job1 is run")
delay(1000)
println("job1 done")
}
val job2 = this.launch(context = this.coroutineContext) {
println("job2 is run")
delay(1000)
println("job2 done")
}
val job3 = this.launch(context = this.coroutineContext) {
println("job3 is run")
delay(1000)
println("job3 done")
}
val job4 = this.launch(context = this.coroutineContext) {
println("job4 is run")
delay(1000)
println("job4 done")
}
job1.join()
job2.join()
job3.join()
job4.join()
}
// 執(zhí)行結(jié)果:
/*
* job1 is run
* job2 is run
* job3 is run
* job4 is run
* job1 done
* job2 done
* job3 done
* job4 done
* */
- CoroutineContext "+" 操作
val what10 = CoroutineScope(Dispatchers.Unconfined).launch {
val childJob = CoroutineScope(Dispatchers.Unconfined + coroutineContext).launch {
println("childJob is run")
delay(1000)
println("childJob done")
}
}
val what10in = what10.cancel()
// 執(zhí)行結(jié)果:childJob is run, 紙打印一句,因?yàn)樽兂闪烁缸訁f(xié)程,父級(jí)取消,suspend的一切跟著取消
// 這里的delay(1000)可以理解成suspend,或者說就是suspend
- CoroutineContext "+Job" 操作
val what11 = runBlocking {
val whatJob1 = Job()
this.launch(context = coroutineContext + whatJob1) {
delay(500)
println("job1 dene")
}
this.launch(context = coroutineContext + whatJob1) {
delay(1000)
println("job2 dene")
}
this.launch(context = Dispatchers.Default + whatJob1) {
delay(1500)
println("job3 done")
}
this.launch(context = Dispatchers.Default + whatJob1) {
delay(2000)
println("job4 done")
}
this.launch(context = Dispatchers.Default + whatJob1) {
delay(4000)
println("job5 done")
}
delay(1888)
whatJob1.cancel()// 取消任務(wù)
}
// 執(zhí)行結(jié)果
/*
* job1 dene
* job2 dene
* job3 dene
* */
- 安全的使用 CoroutineScope,自定義的都比較安全,盡量少用GlobalScope(作用域越小越安全)
比方說:你家里有個(gè)老婆叫紅旗
,我外面有一群女人叫彩旗
,彩旗越少就越安全。但是不刺激啊...
val what12 = SafeCoroutineScope(Dispatchers.Unconfined).launch {
val whatJob1 = Job()
this.launch(context = coroutineContext + whatJob1) {
delay(500)
println("job1 dene")
}
this.launch(context = coroutineContext + whatJob1) {
delay(1000)
println("job2 dene")
}
delay(800)
whatJob1.cancel()
}
- viewModelScope
viewModelScope 會(huì)在對(duì)象銷毀時(shí),自動(dòng)銷毀,也可手動(dòng)提前銷毀優(yōu)化內(nèi)存
internal class MyViewModel : ViewModel() {
val whatScope = viewModelScope
fun fetchData() {
// 在 viewModelScope 中啟動(dòng)一個(gè)新的協(xié)程
whatScope.launch {
// 這里執(zhí)行異步操作,例如網(wǎng)絡(luò)請(qǐng)求或數(shù)據(jù)庫(kù)查詢
val data = performNetworkOperation() // 假設(shè)這是一個(gè)掛起函數(shù)
// 使用數(shù)據(jù)
processData(data)
}
}
private suspend fun performNetworkOperation(): String {
// 這里執(zhí)行網(wǎng)絡(luò)請(qǐng)求或其他耗時(shí)操作
return "new value"
}
private fun processData(data: String) {
// 處理數(shù)據(jù)
whatScope.cancel()
}
}
- Channel 協(xié)程之間的數(shù)據(jù)通訊 Channel
Channel相當(dāng)于之前博客《kotlin多線程》篇章中的BlockingQueue
val what13 = runBlocking {
val channel = Channel<Int>() // 相當(dāng)于BlockingQueue
this.launch(Dispatchers.Default) {
repeat(5){
delay(200)
channel.send(it)
if (it == 4) channel.close()
}
}
this.launch(Dispatchers.Default) {
repeat(5){
try {
val element = channel.receive()
println(element)
}
catch (e:ClosedReceiveChannelException) {
// 必須捕獲這個(gè)異常否則程序會(huì)崩
println("error: ${e.message}")
}
}
}
}
- Channel 管道
// Channel 管道
fun produce1() = SafeCoroutineScope(Dispatchers.Default).produce<Int> {
repeat(5){
send(it) // 發(fā)送0~4
}
}
fun produce2(numbers:ReceiveChannel<Int>) = SafeCoroutineScope(Dispatchers.Default).produce<Int> {
for (x in numbers) {
send(x * x)
}
}
fun produce3(numbers:ReceiveChannel<Int>) = SafeCoroutineScope(Dispatchers.Default).produce<Int> {
for (x in numbers) {
send(x+1)
}
}
fun HongMain() = runBlocking<Unit> {
val numbers1 = produce1()
val numbers2 = produce2(numbers1)
val numbers3 = produce3(numbers2)
numbers3.consumeEach {
println("consume: $it")
}
println("receive done")
// 接收完成之后,關(guān)閉所有produce
numbers3.cancel()
numbers2.cancel()
numbers1.cancel()
}
- channel 緩沖
fun HongCache() = runBlocking {
val cacheSize = 3
val channel = Channel<Int>(cacheSize)// 創(chuàng)建帶有緩沖區(qū)的channel
this.launch(this.coroutineContext) {
repeat(6){
delay(50)
println("send $it")
channel.send(it)
}
}
this.launch {
delay(1000)
repeat(6){
println("receive: ${channel.receive()}")
}
}
}
/*
* 第一個(gè)協(xié)程先發(fā)送兩個(gè)消息,需要等到第二個(gè)協(xié)程時(shí)間到了,第二個(gè)協(xié)程才開始消費(fèi)信息
* 等到緩沖區(qū)的信息消費(fèi)完畢后,第一個(gè)協(xié)程繼續(xù)發(fā)送信息,第二個(gè)協(xié)程繼續(xù)消費(fèi)信息,
* 以此類推直到結(jié)束。
* */
- actor
actor 本身就是一個(gè)協(xié)程,內(nèi)部包含一個(gè)channel,通過channel與其他協(xié)程通訊,這個(gè)內(nèi)部channel只做接收信息
fun HongActor() = runBlocking {
val summer = actor<Int>(context = this.coroutineContext) {
var sm = 0
for (x in channel) {
sm += x
println("sim : $sm")
}
}
repeat(10){
summer.send(it)
}
}
- select
fun produceSelect1(context:CoroutineContext) = SafeCoroutineScope(context).produce<String> {
while (true) {
delay(400)
send("hong")
}
}
fun produceSelect2(context:CoroutineContext) = SafeCoroutineScope(context).produce<String> {
while (true) {
delay(600)
send("lina")
}
}
suspend fun selectProduces(channel1:ReceiveChannel<String>,channel2:ReceiveChannel<String>) {
select<Unit> {
channel1.onReceive {
println(it)
}
channel2.onReceive {
println(it)
}
}
}
fun HongSelectMain() = runBlocking {
val hong = produceSelect1(this.coroutineContext)
val lina = produceSelect2(this.coroutineContext)
repeat(10) {
selectProduces(hong,lina)
}
this.coroutineContext.cancelChildren()
// ok
}
- lifecycleScope + Activity
class MiLifecycleCoroutineScope:AppCompatActivity(){
override fun onStart() {
super.onStart()
}
fun loadData(){
lifecycleScope.launch {
val data = fetchDataFromNetwork()
println(data)
}
}
suspend fun fetchDataFromNetwork():Any {
delay(2000)
return "data"
}
}
// Activity銷毀,lifecycleScope自動(dòng)銷毀,和viewmodelScope類似,屬于綁定關(guān)系的CoroutineScope
- rememberCoroutineScope + @Composable
@Composable
fun MyComposable() {
val coroutineScope = rememberCoroutineScope()
// 在這里,你可以使用 coroutineScope.launch 來啟動(dòng)一個(gè)新的協(xié)程
// 這個(gè)協(xié)程會(huì)在組件銷毀時(shí)自動(dòng)取消,并且會(huì)在每次組件重組時(shí)保持相同的實(shí)例
coroutineScope.launch {
// 在這里執(zhí)行異步操作
}
}
- 自定義的 SafeCoroutineScope
class SafeCoroutineScope(context:CoroutineContext):CoroutineScope,Closeable {
val handler = CoroutineExceptionHandler { context, exception ->
println("異常捕獲并處理: $exception")
}
override val coroutineContext: CoroutineContext = SupervisorJob() + context + handler
override fun close() {
coroutineContext.cancelChildren()
}
}
以上實(shí)乃androidx和kotlinx的內(nèi)功心法,持有它Android在你面前就像褪去衣服的美女,發(fā)揮你的原始獸性去攻略它吧.
thank..