我的代码流如下:
SqlDelight queries ( 2x )
-> Coroutine Channels ( 2x )
-> Coroutine Channel ( 1x )
我注意到Queries
'Channel
正在发出, Presenter 正在接受并且其Channel
正在发出,但是 ViewModel 永远不会收到值。
也永远不会退出在Channels
上进行迭代的for循环,我认为这很好,但是我无法弄清楚为什么从未收到值,所以:/
init {
// When ViewModel is instantiated, start observing for UiModels
launch( IO ) { startObserving() }
}
private suspend fun startObserving() {
channelsAvailability.postLoading()
runCatching {
// Receive Models
for ( uiModel in presenter.observe() )
channelsAvailability.postData( uiModel )
// If some error occurs, notify it, wait and then try again
}.onFailure {
channelsAvailability.postError( it )
delay( DEFAULT_ERROR_DELAY )
startObserving()
}
}
/**
* @return [ReceiveChannel] of [ChannelsAvailabilityUiModel]
* Creates a single [ReceiveChannel] from [hasMovieChannels.observe] and
* [hasTvChannels.observe] [ReceiveChannel]s
*/
suspend fun observe(): ReceiveChannel<ChannelsAvailabilityUiModel> = coroutineScope {
val channel = Channel<ChannelsAvailabilityUiModel>( CONFLATED )
launch( IO ) {
for ( boolean in hasMovieChannels.observe() ) {
// Cache the value
hadMovieChannels = boolean
// Create and send UiModel if both hadMovieChannels and hadTvChannels are not null
maybeMakeModel()?.let { channel.send( it ) }
}
}
launch( IO ) {
for ( boolean in hasTvChannels.observe() ) {
// Cache the value
hadTvChannels = boolean
// Create and send UiModel if both hadMovieChannels and hadTvChannels are not null
maybeMakeModel()?.let { channel.send( it ) }
}
}
channel
}
/**
* @return [ReceiveChannel] of [Boolean] whether any [TvChannel] is present in [LocalData]
* Maps the count of [TvChannels] into a [Boolean] representing whether any Channels is available
*/
suspend fun observe() = localData.observeCountTvChannels().map { it > 0 }
/** @return [ReceiveChannel] of the [Int] count of the stored [TvChannel]s */
override suspend fun observeCountTvChannels() = tvChannels.observeCount()
/**
* @return [ReceiveChannel] of the [Int] count of the stored channels [TvChannelPojo]
* Maps the [Query] into a [ReceiveChannel]
*/
override suspend fun observeCount() = queries.count().asChannel().mapToOne().map { it.toInt() }
fun <T : Any> Query<T>.asChannel(): ReceiveChannel<Query<T>> {
val channel = Channel<Query<T>>( CONFLATED )
// Ensure consumers immediately run the query.
channel.offer(this )
val listener = object : Query.Listener, (Throwable?) -> Unit {
override fun queryResultsChanged() {
channel.offer(this@asChannel )
}
override fun invoke( cause: Throwable? ) {
removeListener(this )
}
}
addListener( listener )
channel.invokeOnClose( listener )
return channel
}
suspend fun <T : Any> ReceiveChannel<Query<T>>.mapToOne( context: CoroutineContext? = null ) =
map(context ?: coroutineContext ) { it.executeAsOne() }