Kotlin—Coroutine(協(xié)程)的基本使用
什么是協(xié)程
在java中異步都會(huì)使用到線程菊卷,在kotlin中引入了協(xié)程的概念。與線程類似烟勋,協(xié)程也是用于處理異步的启摄,不過與線程相比更加輕巧。協(xié)程完全通過編譯技術(shù)實(shí)現(xiàn)实束,使用掛起機(jī)制來實(shí)現(xiàn)異步奥秆,而不會(huì)阻塞線程。協(xié)程是一種避免線程阻塞咸灿、開銷更小且更加可控的異步操作构订。
協(xié)程的基礎(chǔ)使用
創(chuàng)建協(xié)程,有三種方式runBlocking
避矢、launch
悼瘾、async
。
-
runBlocking
創(chuàng)建一個(gè)阻塞的協(xié)程审胸,當(dāng)協(xié)程內(nèi)部代碼執(zhí)行完畢后才會(huì)執(zhí)行后面的代碼亥宿。fun main() { println("Hello") runBlocking { delay(1000) println("World") } println("---end---") }
在
runBlocking
創(chuàng)建的協(xié)程里面,調(diào)用delay
讓此協(xié)程掛起1秒歹嘹。執(zhí)行代碼箩绍,首先打印Hello,1秒后打印World,最后打印—end—尺上。注意delay掛起函數(shù)只有在協(xié)程內(nèi)部才能調(diào)用材蛛。 -
launch
在當(dāng)前協(xié)程作用域下面創(chuàng)建一個(gè)非阻塞子協(xié)程圆到,同時(shí)返回個(gè)Job
對(duì)象,用來控制當(dāng)前協(xié)程卑吭。fun main() = runBlocking { println("hello") launch { delay(1000) println("world") } println("---end---") }
使用
runBlocking
來包裹main
函數(shù)芽淡,函數(shù)整體都處于runBlocking
創(chuàng)建的協(xié)程作用域下面,然后通過launch
創(chuàng)建一個(gè)子協(xié)程豆赏,掛起1秒之后打印輸出挣菲。執(zhí)行代碼發(fā)現(xiàn)首先輸出hello和—end—,等待一秒之后打印world掷邦,如何讓他按順序執(zhí)行白胀。fun main() = runBlocking { println("hello") val job = launch { delay(1000) println("world") } job.join() println("---end---") }
修改代碼,拿到
launch
返回的job對(duì)象抚岗,調(diào)用join()
函數(shù)或杠,此時(shí)會(huì)等待job協(xié)程內(nèi)部的代碼執(zhí)行完畢后才會(huì)往后執(zhí)行。執(zhí)行代碼宣蔚,先打印hello向抢,然后1秒后打印world和—end—。 -
Job
常用方法- isActive
- isCompleted
- isCanceledstart
- cancel
- join
-
GlobalScope.launch
創(chuàng)建一個(gè)全局非阻塞協(xié)程胚委,fun main() { println("hello") GlobalScope.launch { delay(1000) println("world") } Thread.sleep(1500) println("---end---") }
使用
GlobalScope.launch
在任何地方都能創(chuàng)建一個(gè)全局的協(xié)程挟鸠,由于launch
創(chuàng)建的協(xié)程是非阻塞的,所以讓當(dāng)前線程睡眠1.5秒等待協(xié)程執(zhí)行完畢亩冬。與上面代碼打印結(jié)果一樣艘希。由于使用GlobalScope.launch
時(shí),會(huì)創(chuàng)建一個(gè)頂層協(xié)程鉴未。雖然很輕量枢冤,但它運(yùn)行時(shí)仍會(huì)消耗一些內(nèi)存資源鸠姨,所以通常是在協(xié)程內(nèi)部作用域下使用launch
創(chuàng)建協(xié)程铜秆,而不是使用GlobalScope來創(chuàng)建全局協(xié)程。 -
async
創(chuàng)建一個(gè)非阻塞協(xié)程讶迁,同時(shí)返回Deferred
连茧,可通過await()
函數(shù)獲取協(xié)程返回的具體值;fun main() = runBlocking { println("hello") val def = async { delay(1000) "world" } println(def.await()) println("---end---") }
-
掛起函數(shù)
接著上面例子巍糯,將
launch
協(xié)程內(nèi)部的代碼封裝為一個(gè)函數(shù)啸驯,使用suspend
關(guān)鍵,這個(gè)函數(shù)就叫掛起函數(shù)
祟峦,上面的delay
就是一個(gè)封裝好的掛起函數(shù)罚斗。fun main() = runBlocking { println("hello") val job = launch { delay1000() } job.join() println("---end---") } suspend fun delay1000(){ delay(1000) println("world") }
-
調(diào)度器
上面提到三種創(chuàng)建協(xié)程的方式都有一個(gè)選填參數(shù)
CoroutineContext
,協(xié)程的上下文環(huán)境宅楞。也就是協(xié)程調(diào)度器针姿,調(diào)度器確定了協(xié)程執(zhí)行的線程環(huán)境袱吆。-
Dispatchers.Default
默認(rèn)后臺(tái)線程池里的線程 ; -
Dispatchers.Main
Android主線程; -
Dispatchers.IO
后臺(tái)線程池里的IO線程 ; -
Dispatchers.Unconfined
不限制,使用父協(xié)程所屬的線程; -
newSingleThreadContext
使用新的線程距淫。
如需要在協(xié)程中切換調(diào)度器可使用
withContext
绞绒,withContext
返回最后一行代碼的返回值。 -
-
Android中的使用
簡(jiǎn)單了介紹了協(xié)程的基本使用榕暇,寫個(gè)小demo蓬衡,模擬網(wǎng)絡(luò)請(qǐng)求然后再界面展示數(shù)據(jù)。在activity中首先要實(shí)現(xiàn)
CoroutineScope
接口彤枢,然后通過by
關(guān)鍵字將接口的具體實(shí)現(xiàn)委托給MainScope
狰晚,這樣當(dāng)前activity就是一個(gè)協(xié)程作用域了。class MainActivity : AppCompatActivity(), CoroutineScope by MainScope() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) button.setOnClickListener { showData() } } // 創(chuàng)建協(xié)程缴啡,默認(rèn)在當(dāng)前線程家肯,也就是UI線程 private fun showData() = launch { progress.visibility = View.VISIBLE textView.text = loadData() progress.visibility = View.GONE } //網(wǎng)絡(luò)請(qǐng)求 切換為IO線程 private suspend fun loadData()= withContext(Dispatchers.IO) { delay(2000)//掛起2秒 模擬網(wǎng)絡(luò)請(qǐng)求 "假裝這是網(wǎng)絡(luò)請(qǐng)求到的數(shù)據(jù)" //返回請(qǐng)求到的數(shù)據(jù) } }
基本介紹完了協(xié)程的最最最基礎(chǔ)的使用,相比于線程除了內(nèi)存開銷更小盟猖、不會(huì)造成線程阻塞的優(yōu)點(diǎn)之外之外讨衣,個(gè)人覺得在沒有用RxJava的情況下可以少寫很多線程的創(chuàng)建、切換式镐、異步接口的回調(diào)反镇。讓代碼更加干凈清爽。
個(gè)人學(xué)習(xí)筆記娘汞,如有錯(cuò)誤不對(duì)的地方歡迎各位大佬指出歹茶。