開發(fā)過程中初坠,有時候需要從網(wǎng)絡(luò)上下載數(shù)據(jù)甘畅,并刷新界面瘦黑。
fun init(){
val userInfo = fetchUserInfo() // 網(wǎng)絡(luò)請求
refreshUI(userInfo) // 刷新UI
}
fetchUserInfo是比較耗時的操作,會一直阻塞當(dāng)前線程直到數(shù)據(jù)返回端幼。
在android項目中,為了避免阻塞UI線程造成anr弧满,都是新開線程去執(zhí)行耗時的操作婆跑,獲取到執(zhí)行結(jié)果后返回UI線程繼續(xù)執(zhí)行余下的操作。
修改后的代碼:
發(fā)起網(wǎng)絡(luò)請求時注冊callBack方法庭呜,當(dāng)完成網(wǎng)絡(luò)請求后滑进,在UI線程回掉callBack方法犀忱。
fun init(){
fetchUserInfo(callBack = { userInfo ->
refreshUI(userInfo) // 刷新UI
}) // 網(wǎng)絡(luò)請求
}
這樣可以避免anr的產(chǎn)生。但是一些復(fù)雜的情況下容易產(chǎn)生“callBack hell”扶关,比如網(wǎng)絡(luò)請求返回后阴汇,先存入本地數(shù)據(jù)庫(耗時操作),在進(jìn)行UI刷新操作节槐。
fun init(){
fetchUserInfo(callBack = { userInfo ->
saveUserInfo(userInfo){ userInfo ->
refreshUI(userInfo) // 刷新UI
}
}) // 網(wǎng)絡(luò)請求
}
callback層層嵌套搀庶,代碼將會橫向擴(kuò)展,十分臃腫铜异。
為了解決代碼橫向擴(kuò)展帶來的問題哥倔,可以借助RxJava,使橫向擴(kuò)展轉(zhuǎn)變?yōu)榭v向擴(kuò)展熙掺。
fun init() {
saveUserInfo(userInfo)
.flatMap{ userInfo ->
saveUserInfo(userInfo)
}.subscribe{ userInfo ->
refreshUI(userInfo)
}
}
但是Rxjava學(xué)習(xí)成本高昂未斑,其次代碼變得不直觀明朗,一眼望去全是flatMap等操作符币绩。而且RxJava是單參數(shù)傳遞蜡秽,如果我想同時傳遞兩個參數(shù),必須新建一個包裝類缆镣。
以上兩種方式都不是完美的解決方法芽突。我們的終極目標(biāo)是以寫同步代碼的方式書寫異步代碼。
就像這樣
fun init(){
val userInfo = fetchUserInfo() // 網(wǎng)絡(luò)請求
refreshUI(userInfo) // 刷新UI
}
當(dāng)然上邊只是理想情況董瞻,我們需要讓編譯器知道哪些代碼是異步的寞蚌,是需要后臺執(zhí)行的,需要和同步代碼區(qū)分開來钠糊。
kotlin的最終實現(xiàn)方案:
fun init(){
launch {
val userInfo = async {
fetchUserInfo() // 網(wǎng)絡(luò)請求
}
refreshUI(userInfo.await()) // 刷新UI
}
}
launch創(chuàng)造了一個協(xié)程執(zhí)行的上下文挟秤,async代碼塊包裹的是需要異步執(zhí)行的任務(wù),當(dāng)執(zhí)行userInfo.await()時抄伍,代碼會暫停執(zhí)行(但是不阻塞UI線程)艘刚,直到網(wǎng)絡(luò)請求返回后再執(zhí)行refreshUI的操作。
下一篇會詳細(xì)介紹kotlin coroutines實現(xiàn)機(jī)制截珍。