Size: a a a

Kotlin Community

2020 July 08

L

L in Kotlin Community
Alexander Nozik
подписка должна отмениться потому что отменятся родительский скоуп подписки, но тут я не на 100% уверен.
У меня было странное поведение когда именно BroadcastChannel подвешивался в рантайме при схлопывании одного из скоупов, который содержал ReceiveChannel полученный из этого BroadcastChannel, в итоге все остальные подписки переставали получать данные. Лечилось как раз таки через ReceiveChannel.cancel, хотя тоже нет уверенности что это правильное поведение, логично было бы то как вы описали :)
источник

A

Aleksandr in Kotlin Community
val channel = BroadcastChannel<String>(Channel.BUFFERED)
val channel = BroadcastChannel<String>(Channel.CONFLATED)
источник

A

Aleksandr in Kotlin Community
ну или с capacity не меньше чем подписчиков
источник

AN

Alexander Nozik in Kotlin Community
Aleksandr
val channel = BroadcastChannel<String>(Channel.BUFFERED)
val channel = BroadcastChannel<String>(Channel.CONFLATED)
Это не решение проблемы. Это обход дедлока, а не разрешение
источник

МR

М R in Kotlin Community
давайте больше контекста дам, у меня вот такой event bus работает на этом.
И закрывать канал не получится просто так, так как нужно закрывать его, когда отменяется подписка, а об этом знает только место вызова.
class Bus<T> {
   private val channel: BroadcastChannel<T> = BroadcastChannel(1)

   fun content(): Flow<T> {
       return channel.openSubscription().receiveAsFlow()
   }

   fun setValue(value: T) {
       channel.offer(value)
   }
}
источник

AN

Alexander Nozik in Kotlin Community
М R
давайте больше контекста дам, у меня вот такой event bus работает на этом.
И закрывать канал не получится просто так, так как нужно закрывать его, когда отменяется подписка, а об этом знает только место вызова.
class Bus<T> {
   private val channel: BroadcastChannel<T> = BroadcastChannel(1)

   fun content(): Flow<T> {
       return channel.openSubscription().receiveAsFlow()
   }

   fun setValue(value: T) {
       channel.offer(value)
   }
}
Ну это понятно. Кстати, это в сторону, но сюда прям просится StateFlow.
источник

AN

Alexander Nozik in Kotlin Community
Там вообще все пргоблемы с закрытием отпадают
источник

МR

М R in Kotlin Community
StateFlow эмитит текущее значение при подписке
источник

AN

Alexander Nozik in Kotlin Community
М R
StateFlow эмитит текущее значение при подписке
да,  тут у вас то же самое, будет эмитить то, что загружено в канал
источник

AN

Alexander Nozik in Kotlin Community
Если надо пропустить первый элемент, то всегда можно на сделать  drop(1)
источник

МR

М R in Kotlin Community
Alexander Nozik
да,  тут у вас то же самое, будет эмитить то, что загружено в канал
в том и дело, что нет
Вот такой вариант ничего не напечатает
val channel = BroadcastChannel<String>(1)
channel.send("0")

scope.launch {
   channel.openSubscription()
       .receiveAsFlow()
       .collect { println("receive $it") }
}

scope.launch {
   channel.openSubscription()
       .receiveAsFlow()
       .collect { println("receive $it") }
}
источник

L

L in Kotlin Community
М R
давайте больше контекста дам, у меня вот такой event bus работает на этом.
И закрывать канал не получится просто так, так как нужно закрывать его, когда отменяется подписка, а об этом знает только место вызова.
class Bus<T> {
   private val channel: BroadcastChannel<T> = BroadcastChannel(1)

   fun content(): Flow<T> {
       return channel.openSubscription().receiveAsFlow()
   }

   fun setValue(value: T) {
       channel.offer(value)
   }
}
Здесь просто добавь onCompletion на созданный тобой Flow с помощью receiveAsFlow прямо внутри функции где отмени созданную с помощью openSubscription подписку, будет работать
источник

L

L in Kotlin Community
А вообще да, @noraltavir прав и StateFlow призваны как раз таки заменить conflated broadcast channel
источник

AN

Alexander Nozik in Kotlin Community
М R
в том и дело, что нет
Вот такой вариант ничего не напечатает
val channel = BroadcastChannel<String>(1)
channel.send("0")

scope.launch {
   channel.openSubscription()
       .receiveAsFlow()
       .collect { println("receive $it") }
}

scope.launch {
   channel.openSubscription()
       .receiveAsFlow()
       .collect { println("receive $it") }
}
Ну сделайте дроп первого элемента в результирующем flow
источник

L

L in Kotlin Community
https://pl.kotl.in/Z_efcveNJ вот поиграться, закоменчены другие варианты отмены, к сожалению код дедлочится, желательно запускать на локале чтоб увидеть весь аутпут. При firstSubscription.cancel() или onCompletion все работает ок, при firstJob.cancel или firstScope.cancel все зависает :)
источник

AN

Alexander Nozik in Kotlin Community
М R
в том и дело, что нет
Вот такой вариант ничего не напечатает
val channel = BroadcastChannel<String>(1)
channel.send("0")

scope.launch {
   channel.openSubscription()
       .receiveAsFlow()
       .collect { println("receive $it") }
}

scope.launch {
   channel.openSubscription()
       .receiveAsFlow()
       .collect { println("receive $it") }
}
Кстати и без дропа работает как вы хотите: https://pl.kotl.in/htUDlz_kq
источник

AN

Alexander Nozik in Kotlin Community
а, нет, вру, дроп нужен: https://pl.kotl.in/dNJ-bpEin
источник

МR

М R in Kotlin Community
L
А вообще да, @noraltavir прав и StateFlow призваны как раз таки заменить conflated broadcast channel
у них есть различия в поведении
источник

AH

Ayrat Hudaygulov in Kotlin Community
Товарищи, как заставить подобное работать при условии что нельзя избавиться от дженерика? (пример специально упрощён)
источник

AH

Ayrat Hudaygulov in Kotlin Community
ex as E жалуется на unchecked cast
источник