-
Notifications
You must be signed in to change notification settings - Fork 1.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ClosedSendChannelException for callbackFlow #1770
Comments
Reason is that
brackets gets executed. So you start your timer and In order to keep your
The I agree, that it is not very intuitive, especially for former RxJava users, where Perhaps having callbackFlow behave, like Observable.create would be a good thing (and PS: edited wrong function name |
The above answer is generally correct.
This is done intentionally to indicate that not only consumer can complete the flow, but it also can be cancelled (and then callback should be unregistered). |
Maybe
And it would be 'always active' by default. Again, I'm not sure. Just food for thought. |
Thanks for the input. We were considering a similar API initially but found that it has a way too many limitations. Consider the following example from the documentation:
To properly close this API, you should have an instance of the It can be changed to But what if both |
Thanks for the heads up guys. It makes sense now. Coming from RxJava I would expect a behavior similar to observable where you manually have to call Now that the rationale was given I'm not sure if it is a good idea to make channelFlow/callbackFlow stay alive by default or if it should even be intended as an observable alternative. You guys decide that.
Yes that would be a good idea I think. So I modified a little bit my code to look like this: @ExperimentalCoroutinesApi
class TimerFlow private constructor(millisInFuture: Long, countDownInterval: Long) {
private val tick: Flow<Long> = callbackFlow {
if (Looper.myLooper() == null) {
throw IllegalStateException("Can't create TimerFlow inside thread that has not called Looper.prepare() Just use Dispatchers.Main")
}
object : CountDownTimer(millisInFuture, countDownInterval) {
override fun onFinish() {
cancel()
}
override fun onTick(millisUntilFinished: Long) {
offer(millisUntilFinished)
}
}.start()
awaitClose()
}
companion object {
/**
* Create a [Flow] that will be a countdown until a specified time in the future.
*
* @param millisInFuture The milliseconds in the future that this will countdown to.
* @param countDownInterval The minimum amount of time between emissions.
*/
@JvmStatic
fun create(millisInFuture: Long, countDownInterval: Long) =
TimerFlow(millisInFuture, countDownInterval).tick
}
} Afterwards I call the above class like this: class MainActivity : AppCompatActivity() {
@ExperimentalCoroutinesApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnStart.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
setCountDown(5000, 1000)
}
}
}
@ExperimentalCoroutinesApi
private suspend fun setCountDown(millisInFuture: Long, countDownInterval: Long) {
TimerFlow.create(millisInFuture, countDownInterval).collect {
Log.i("main", it.toString())
textView.text = it.toString()
}
}
} |
I'm trying to convert an Android CountDownTimer into emitting values via
callbackFlow
but it throwsClosedSendChannelException: Channel was closed
when collecting the items. I can't really find a valid reason why this happens.Is this a bug or am I missing something? I'm new to Coroutines.
The text was updated successfully, but these errors were encountered: