Kotlin 生产<;类型>;vs频道<;类型>;()
试图理解频道。我想为android BluetoothLeScanner提供渠道。为什么这样做有效:Kotlin 生产<;类型>;vs频道<;类型>;(),kotlin,coroutine,kotlin-coroutines,kotlinx.coroutines.channels,Kotlin,Coroutine,Kotlin Coroutines,Kotlinx.coroutines.channels,试图理解频道。我想为android BluetoothLeScanner提供渠道。为什么这样做有效: fun startScan(filters: List<ScanFilter>, settings: ScanSettings = defaultSettings): ReceiveChannel<ScanResult?> { val channel = Channel<ScanResult>() scanCallback = object :
fun startScan(filters: List<ScanFilter>, settings: ScanSettings = defaultSettings): ReceiveChannel<ScanResult?> {
val channel = Channel<ScanResult>()
scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
channel.offer(result)
}
}
scanner.startScan(filters, settings, scanCallback)
return channel
}
fun startScan(过滤器:列表,设置:扫描设置=默认设置):接收频道{
val通道=通道()
scanCallback=对象:scanCallback(){
覆盖扫描结果(回调类型:Int,结果:ScanResult){
频道。报价(结果)
}
}
scanner.startScan(过滤器、设置、扫描回调)
返回通道
}
但不是这个:
fun startScan(scope: CoroutineScope, filters: List<ScanFilter>, settings: ScanSettings = defaultSettings): ReceiveChannel<ScanResult?> = scope.produce {
scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
offer(result)
}
}
scanner.startScan(filters, settings, scanCallback)
}
fun startScan(作用域:CoroutineScope,过滤器:List,设置:ScanSettings=defaultSettings):ReceiveChannel=scope.product{
scanCallback=对象:scanCallback(){
覆盖扫描结果(回调类型:Int,结果:ScanResult){
报价(结果)
}
}
scanner.startScan(过滤器、设置、扫描回调)
}
它告诉我当它想第一次呼叫报价时,频道已关闭
EDIT1:根据文档:当协同程序完成时,频道关闭。
这很有意义。我知道我们可以使用suspendCoroutine
和resume
进行一次性回调
-替换。然而,这是一种侦听器/流的情况。我不希望协同程序使用product
来完成,因为您将范围引入了您的频道。这意味着,可以取消产生通过通道传输的项目的代码
这也意味着频道的生存期从产品的lambda开始,并在该lambda结束时结束
在您的示例中,product
调用的lambda几乎立即结束,这意味着您的频道几乎立即关闭
将代码更改为以下内容:
fun CoroutineScope.startScan(filters: List<ScanFilter>, settings: ScanSettings = defaultSettings): ReceiveChannel<ScanResult?> = produce {
scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
offer(result)
}
}
scanner.startScan(filters, settings, scanCallback)
// now suspend this lambda forever (until its scope is canceled)
suspendCancellableCoroutine<Nothing> { cont ->
cont.invokeOnCancellation {
scanner.stopScan(...)
}
}
}
...
val channel = scope.startScan(filter)
...
...
scope.cancel() // cancels the channel and stops the scanner.
确保您的协同程序作用域的生命周期与蓝牙扫描仪的作用域相匹配。如果你有一个活动绑定的作用域,但它不适合,那么创建一个不同的作用域。我使用了作用域,没有区别。我猜问题在于scanner.startscan之后生成返回,这意味着它已完成product
调用立即返回,它返回的值是您需要从中使用数据的ReceiveChannel
。但是是的,product
块应该是一个无限循环,将数据推送到通道中。在您的情况下,product
块立即完成。因此,您的第一个示例与基于回调的方法更为匹配。product
不是您在此场景中想要的。对于这样的用例,您需要一个合适的通道。因此,虽然这似乎是“如何保持product块提供的协同程序保持活动”的解决方案,但我认为Marko和gMale是正确的,product根本不是这种情况下的正确工具。还有一票!我认为使用product
是一个好方法。您还可以使用product以结构化的方式处理错误。我更新了上面的答案,以展示一个以结构化方式处理错误的示例。
fun CoroutineScope.startScan(filters: List<ScanFilter>, settings: ScanSettings = defaultSettings): ReceiveChannel<ScanResult?> = produce {
// Suspend this lambda forever (until its scope is canceled)
suspendCancellableCoroutine<Nothing> { cont ->
val scanCallback = object : ScanCallback() {
override fun onScanResult(callbackType: Int, result: ScanResult) {
offer(result)
}
override fun onScanFailed(errorCode: Int) {
cont.resumeWithException(MyScanException(errorCode))
}
}
scanner.startScan(filters, settings, scanCallback)
cont.invokeOnCancellation {
scanner.stopScan(...)
}
}
}