Kotlin 生产<;类型>;vs频道<;类型>;()

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 :

试图理解频道。我想为android BluetoothLeScanner提供渠道。为什么这样做有效:

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(...)
        }
    }
}