Произвести<тип> против канала<тип>()

Пытаюсь понять каналы. Я хочу направить 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(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)
}

Он сообщает мне Channel was closed, когда хочет вызвать offer в первый раз.

РЕДАКТИРОВАТЬ1: Согласно документам: The channel is closed when the coroutine completes. что имеет смысл. Я знаю, что мы можем использовать suspendCoroutine с resume для одноразовой callback-замены. Однако это ситуация слушателя/потока. Я не хочу, чтобы сопрограмма завершалась

Убедитесь, что жизненный цикл области сопрограммы соответствует области действия сканера Bluetooth. Если у вас есть область, связанная с деятельностью, и она не подходит, создайте другую.

Marko Topolnik 23.05.2019 07:36

Я играл с прицелами, никакой разницы. Я предполагаю, что проблема в том, что производит возврат после scan.startscan, что означает, что он завершен

Kuno 23.05.2019 08:15

Вызов produce возвращается немедленно, и возвращаемое им значение — это ReceiveChannel, из которого вам нужно использовать данные. Но да, блок produce должен быть бесконечным циклом, который помещает данные в канал. В вашем случае блок produce завершается сразу. Таким образом, ваш первый пример лучше соответствует вашему подходу, основанному на обратном вызове.

Marko Topolnik 23.05.2019 08:34
produce это не то, что вам нужно в этом сценарии. Для такого варианта использования вам нужен правильный канал.
gMale 25.05.2019 07:12
3 метода стилизации элементов HTML
3 метода стилизации элементов HTML
Когда дело доходит до применения какого-либо стиля к нашему HTML, существует три подхода: встроенный, внутренний и внешний. Предпочтительным обычно...
Формы c голосовым вводом в React с помощью Speechly
Формы c голосовым вводом в React с помощью Speechly
Пытались ли вы когда-нибудь заполнить веб-форму в области электронной коммерции, которая требует много кликов и выбора? Вас попросят заполнить дату,...
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Стилизация и валидация html-формы без использования JavaScript (только HTML/CSS)
Будучи разработчиком веб-приложений, легко впасть в заблуждение, считая, что приложение без JavaScript не имеет права на жизнь. Нам становится удобно...
Flatpickr: простой модуль календаря для вашего приложения на React
Flatpickr: простой модуль календаря для вашего приложения на React
Если вы ищете пакет для быстрой интеграции календаря с выбором даты в ваше приложения, то библиотека Flatpickr отлично справится с этой задачей....
В чем разница между Promise и Observable?
В чем разница между Promise и Observable?
Разберитесь в этом вопросе, и вы значительно повысите уровень своей компетенции.
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Что такое cURL в PHP? Встроенные функции и пример GET запроса
Клиент для URL-адресов, cURL, позволяет взаимодействовать с множеством различных серверов по множеству различных протоколов с синтаксисом URL.
0
4
313
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Используя produce, вы расширяете возможности своего канала. Это означает, что код, создающий элементы, передаваемые по каналу, может быть отменен.

Это также означает, что время жизни вашего канала начинается с начала лямбды produce и заканчивается, когда эта лямбда заканчивается.

В вашем примере лямбда вашего звонка produce заканчивается почти сразу, что означает, что ваш канал закрывается почти сразу.

Измените свой код на что-то вроде этого:

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.

Я добавил строку suspendCancellableCoroutine<Nothing> { ... }, чтобы приостановить действие «навсегда».

Обновление: использование produce и обработка ошибок структурированным образом (позволяет структурированный параллелизм):

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

Итак, хотя это кажется решением «как сохранить сопрограмму, заданную блоком продукта», я думаю, что Марко и gMale правы, и продукт просто не подходит для этой ситуации. Есть еще голосование!

Kuno 28.05.2019 08:48

Я думаю, что использование produce — это хороший способ. Вы также можете обрабатывать ошибки структурированным образом, используя product. Я обновляю свой ответ выше, чтобы показать пример с структурированной обработкой ошибок.

Streets Of Boston 29.05.2019 21:44

Другие вопросы по теме