在這之前的26天,我們所用的都是我要做什麼事,就是做什麼事,不會有條件的選取,但作為android的開發者,一定聽過google爸爸說過single trust of source,在架構的最佳化那篇裡面,他以最近是否活躍為判斷連結
那我這邊再多舉一個例子,假如你的應用可以離線存取,同時也有客製排序的功能,用戶對這項功能的調整頻率較低,你可能會選擇遠端一份排序邏輯資料,本地一份排序邏輯資料
如果是用戶自己設置,你可以在設置確認後先發到遠端,在更新本地,如果是slope one的算法你可以考慮用workManager去定時fetch等等,在多種情境下我們會做不同的考量,而我這裡在系列文裡最後介紹一個coroutine的工具,select
中文好像叫多路復用,其實他就是會先拿到最早available的值
viewModelScope.launch {
val orderRule = select<OrderResponse<OrderRule>> {
async { localOrder() }.onAwait{ OrderResponse(it, isLocal = true)}
async { remoteOrder() }.onAwait{ OrderResponse(it, isLocal = false) }
}
}
而select也可以接收多個channel,通常我們一個channel是可以由多個priducer和多consumer對吧
但如果需求是要同時接收不同的channel呢? 最適合的工具就是select了
suspend fun selectFizzBuzz(fizz: ReceiveChannel<String>, buzz: ReceiveChannel<String>) {
select<Unit> { // <Unit> 意味着该 select 表达式不返回任何结果
fizz.onReceive { value -> // 这是第一个 select 子句
println("fizz -> '$value'")
}
buzz.onReceive { value -> // 这是第二个 select 子句
println("buzz -> '$value'")
}
}
}
val fizz = fizz()//啟動一個corotine,每0.3秒丟值
val buzz = buzz()//啟動一個corotine,每0.5秒丟值
repeat(7) {
selectFizzBuzz(fizz, buzz)
}//蒐集7個
coroutineContext.cancelChildren() //取消兩個coroutine
文檔給的範例非常簡單,我也就不多加敘述了
那如果蒐集到已經close的channel要怎麼半?
onReceive會throw Exception,但我們可以用onReceiveOrNull,如果channel close他會回傳null,而我們可以再對此作處理
b.onReceiveOrNull { value ->
if (value == null)
"Channel 'b' is closed"
else
"b -> '$value'"
}
而channel的接收和發送都會suspend,select也能對發送做出處理
fun CoroutineScope.produceNumbers(side: SendChannel<Int>) = produce<Int> {
for (num in 1..10) { // 生产从 1 到 10 的 10 个数值
delay(100) // 延迟 100 毫秒
select<Unit> {
onSend(num) {} // 发送到主通道
side.onSend(num) {} // 或者发送到 side 通道
}
}
}
大概醬,因為再channel的consume那邊,可能會執行其他動作,這時select救苦以盡快將值發給已經可以此用的channel裡面
其實select是個比較少用到的功能,但聽我講講你也不吃虧,先有個概念,未來遇到問題時也能很快聯想到這個解法,今天就這樣,掰掰