未从协程的渠道收到的值

时间:2019-03-02 05:17:02

标签: kotlin jvm coroutine channels

我的代码流如下: SqlDelight queries ( 2x )-> Coroutine Channels ( 2x )-> Coroutine Channel ( 1x )

我注意到Queries'Channel正在发出, Presenter 正在接受并且其Channel正在发出,但是 ViewModel 永远不会收到值。

也永远不会退出在Channels上进行迭代的for循环,我认为这很好,但是我无法弄清楚为什么从未收到值,所以:/

ViewModel

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

0 个答案:

没有答案