android 如果一段时间后流没有发出任何值,则从流中发出(正在加载)值

8cdiaqws  于 2023-05-12  发布在  Android
关注(0)|答案(2)|浏览(190)

我有一个返回Flow<Status<T>>的存储库。Status可以是Status.LoadingStatus.Data。我希望Flow发出Status.Loading,以防存储库在X毫秒内没有返回任何值。
在伪代码中,它看起来像这样:

repository.fetchData()
  .emitDataOrLoading()
  .collect {
     when (it) {
       is Status.Loading -> // show loading indicator in case no data after X milliseconds
       is Status.Data -> // We have received the data.
     } 
  }

所以我正在寻找如何实现.emitDataOrLoading()

e4yzc0pl

e4yzc0pl1#

这可以使用channelFlow构建器创建。也许有一个更简单的方法与运营商,但我不能拿出一个。
如果fetchData()是一个一次性的挂起函数,你可以这样做:

fun <R, T: R, L: R> lazyFlowOfWithLoadStateIfDelayed(cutoffDelay: Long, loadState: L, fetchAction: suspend ()->T): Flow<R> = channelFlow {
    val sendLoadingJob = launch {
        delay(cutoffDelay)
        send(loadState)
    }
    val value = fetchAction()
    sendLoadingJob.cancelAndJoin()
    send(value)
}

使用方法:

lazyFlowOfWithLoadStateIfDelayed(1000L, Status.Loading) { Status.Data(repository.fetchData()) }
  .collect {
     when (it) {
       is Status.Loading -> // show loading indicator in case no data after X milliseconds
       is Status.Data -> // We have received the data.
     } 
  }

编辑:基于Vivek Gupta的评论和回答,使用debounce构建上述代码的简单方法。这是因为debounce被优化为立即通过有限流的最后一个值。

fun <R, T: R, L: R> lazyFlowOfWithLoadStateIfDelayed(cutoffDelay: Long, loadState: L, fetchAction: suspend ()->T): Flow<R> = flow {
    emit(loadState)
    emit(fetchAction())
}.debounce(cutoffDelay)

如果fetchData()是一个流,例如一个无限监视数据库的流,你可以构建一个流操作符,它只在第一项很慢的情况下显示加载状态:

fun <R, T: R, L: R> Flow<T>.emitLoadStateIfDelayed(cutoffDelay: Long, loadState: L): Flow<R> = channelFlow {
    val sendLoadingJob = launch {
        delay(cutoffDelay)
        send(loadState)
    }
    collect {
        sendLoadingJob.cancelAndJoin()
        send(it)
    }
}
oyxsuwqo

oyxsuwqo2#

假设你有一个密封的类

sealed class Status<out T> {
    object Loading : Status<Nothing>()
    data class Data<T>(val data: T) : Status<T>()
}

你的fetchData()是

suspend fun <T> fetchData(): T {
        delay(Random.nextLong(1000, 5000)) // simulate network delay
    return "data" as T
}

和Repository方法,它正在发出流

fun <T> getData(): Flow<Status<T>> = flow {
    emit(Status.Loading)
    val data = fetchData<T>()
    emit(Status.Data(data))
}

你可以使用debounce创建一个超时的方法。它不会延迟成功的响应。getDataWithLoadingDelay只会延迟getData流中数据值的初始发射,直到经过去抖动时间为止。一旦getData流发出了一个数据值,它将立即被getDataWithLoadingDelay流作为Status.Data值发出,没有任何进一步的延迟。

fun <T> getDataWithLoadingDelay(delayMs: Long): Flow<Status<T>> = getData<T>()
    .debounce(delayMs)
    .map { status ->
        if (status is Status.Data) {
            status
        } else {
            Status.Loading
        }
    }

如果在去抖动期间没有发出任何值,它将发出一个Status.Loading值,以指示仍在提取数据。

相关问题