Android — Kotlin Flow 如何使用callbackflow

JLin
6 min readApr 6, 2020

--

今天如果我想要在使用一個locatoinManager然後進行requestSingleUpdate的callback後才開始觸發flow的流程開怎麼辦?

使用callbackFlow

重點是

必須透過offer進行emit

然後結尾必須透過awaitClose進行unregister你的callback,如果沒有呼叫會產生Exception

如此就可以透過flow 產生callback後才開始進行flow的運作,就可以取代rxJava的emitter傳遞

如何更簡化寫法?

當未來有多個callback要結合進行flow串聯應用的時候,不可能把這樣寫在一個function吧? 所以如果把flow callback定義成一個變數,接著在使用時候在透過collect讓他執行,就可以簡化呼叫方法

為什麼會有多出一個offer方法?

其實這是callbackFlow裡面的方法,你可以透過this的方式在IDE看到,他是一個裡面channel的方法

this.channel.offer("HI")

至於有這麼方法的原理?我還需要研究一下,如果知道的也方便告訴我一下

2020/07/27

更新一下,如果今天你有多個callback要處理,那該如何運用callbackflow進行操作?

使用flatMapConcat

根據研究之後,可以透過 flatMapConcat 進行連接多個flow操作

情境:譬如

  1. 今天想要先掃描藍芽,找到特定裝置
  2. 找到後開始一個藍芽service等待 serviceConnection連接成功
  3. 將service傳到第三個程序,在此程序開始針對藍芽進行連接
  4. 透過此程序進行檢查是否有連接成功.

這時候該怎麼做?

emit使用offer

先講答案

GlobalScope.launch {
startScanDeviceFlow(context)
.flatMapConcat {
startGetBleServiceFlow(context,it)
}.flatMapConcat {
startConnectBleDeviceFlow(context,it)
}.collect{
onConnectedDone()
}
}

個人是先透過這樣測是配合callbackflow看起來是沒問題,但這樣適不適合這樣寫,還需要研究一下

先針對flow 1

@ExperimentalCoroutinesApi
private fun startScanDeviceFlow(context:Context) = callbackFlow<BluetoothDevice?>{
blenManager.startScan{
blenManager.stopScan()
val device = bleScanManager.mLeDevices.first()
offer(device)
}
awaitClose{
// unregister

}
}

可以看到startScan後因為是透過本身的lambada callback,所以這邊使用 callbackflow就很適合,記得awaitClose都必須加上處理

另外flow2 就可以透過

@ExperimentalCoroutinesApi
private fun startGetBleServiceFlow(context: Context, device: BluetoothDevice?) = callbackFlow<BluetoothLeService?>{
foundDevice = device as BluetoothDevice
mServiceConnection = object : ServiceConnection {
override fun onServiceConnected(componentName: ComponentName, service: IBinder) {
val leService = (service as BluetoothLeService.LocalBinder).service

offer(leService)
}

override fun onServiceDisconnected(componentName: ComponentName) {

}
}

val intent = Intent(context, BluetoothLeService::class.java)
context.bindService(intent, mServiceConnection!!, Context.BIND_AUTO_CREATE)

awaitClose {
// unregister

}
}

接下來流程就不多加描述,都是一樣的程序,就可以把多個callback進行串接使用,直到流程完成後結束。

另外是如果要取消需要搭配

cancel

並且在

awaitClose

的地方進行失敗後取消註冊的動作(注意awaitClose會在flow被cancle和onComplation才會執行)

透過多個callback flow + flatMapConcat 就可以整合出多個callback的處理方法

可以看到startFlow的地方透過結合多個callback進行處理,程序就會很清楚乾淨

如果有任何使用錯誤或者該修正的也歡迎告知,謝謝

參考

很清楚的flow 說明

--

--

JLin
JLin

Written by JLin

台中 / JAVA / Android /Kotlin / Kotlin Native 對於Kotlin衍生的JVM等技術 Compose for web / desktop / Ktor Server,生成式AI (Gemini/OpenAI)各式應用, 都有小興趣

No responses yet