这些flow常见API的使用,你一定需要掌握!(五)-灵析社区

德州安卓

stateIn()转变为热流StateFlow

public fun <T> Flow<T>.stateIn(
    scope: CoroutineScope,
    started: SharingStarted,
    initialValue: T
): StateFlow<T> {
    val config = configureSharing(1)
    val state = MutableStateFlow(initialValue)
    val job = scope.launchSharing(config.context, config.upstream, state, started, initialValue)
    return ReadonlyStateFlow(state, job)
}
  • 冷流flow: 只有调用collect{}方法才会触发冷流执行
  • 热流:热流的执行不依赖是否添加观察者

stateIn()将冷流转换为热流StateFlow(),这个流有几个特点:

  1. 需要给予一个初始值
  2. 是一个粘性流,类似于LiveData,会重放最后一个更新数据
  3. 过滤重复数据,也就是说,发送重复数据会进行丢弃
  4. 提供value属性获取内部的值

一般可以用作替代LiveData,直接使用热流作为ViewModel中可观察的数据源,LiveData能实现的它都能实现,不能实现的它也都能实现。

stateIn()转变为热流SharedFlow

public fun <T> Flow<T>.shareIn(
    scope: CoroutineScope,
    started: SharingStarted,
    replay: Int = 0
): SharedFlow<T> {
    val config = configureSharing(replay)
    val shared = MutableSharedFlow<T>(
        replay = replay,
        extraBufferCapacity = config.extraBufferCapacity,
        onBufferOverflow = config.onBufferOverflow
    )
    @Suppress("UNCHECKED_CAST")
    val job = scope.launchSharing(config.context, config.upstream, shared, started, NO_VALUE as T)
    return ReadonlySharedFlow(shared, job)
}

这个就是将冷流flow转换为SharedFlow,上面的热流StateFlow实现了SharedFlow,它主要有以下几个特点:

  1. 无法通过.value的方式访问内部值
  2. 通过replay参数自定义你需要的粘性或非粘性的热流
两种冷流都需要传递一个SharingStarted类型的参数,这个参数有三种类型:Eagerly、Lazily、WhileSubscribed决定热流的启动模式,这里主要介绍WhileSubscribed:
public fun WhileSubscribed(
    stopTimeoutMillis: Long = 0,
    replayExpirationMillis: Long = Long.MAX_VALUE
): SharingStarted =
    StartedWhileSubscribed(stopTimeoutMillis, replayExpirationMillis)
  • stopTimeoutMillis:这个参数指定一个在最后一个订阅者取消和停止流执行的时间间隔,意思就是当最后一个个订阅者取消后,隔stopTimeoutMillisms之后再停止流的执行。
  • replayExpirationMillis:这个参数指定一个再停止流执行和清除流缓存的时间间隔,也就是当停止流执行后,间隔replayExpirationMillisms去清楚流的缓存。

举个应用场景,当应用横竖屏切换时,订阅者就会被取消,但是没必要去停止流执行或者清理缓存,因为横竖屏过后很快就会重建重新显示,这样能更快的刷新界面数据。

retryWhen{}

public fun <T> Flow<T>.retryWhen(predicate: suspend FlowCollector<T>.(cause: Throwable, attempt: Long) -> Boolean): Flow<T> =
    flow {
        var attempt = 0L
        var shallRetry: Boolean
        do {
            shallRetry = false
            val cause = catchImpl(this)
            if (cause != null) {
                if (predicate(cause, attempt)) {
                    shallRetry = true
                    attempt++
                } else {
                    throw cause
                }
            }
        } while (shallRetry)
    }

这个方法也很有用,出现异常时进行重试,并决定是否重试还是弹出提示信息,比如当我们进行网络请求时,请求失败就可以使用这个方法,一方面在retryWhen{}方法中记录错误信息并通知下游流,一方面选择是否进行网络重试。

比如下面这个例子:

fun test2() {
    GlobalScope.launch {
        flow {
            emit("${10 / 随机数}")
        }
            .retryWhen { cause, attempt ->
                if (cause is ArithmeticException && attempt < 3) {
                    emit("retry")
                    true
                } else {
                    false
                }
            }.collect {
                println("jja: $it")
            }
    }
}

当上面的随机数出现0是就会触发ArithmeticException异常,这样retryWhen{}就能捕获并可以尝试重试,随机一个非0且能被整除的数,并且限制了重试次数为3次以为。

总结

关于flow常见api系列文章陆陆续续写了五篇了,暂时就告一段落,基本上常用的都介绍了一遍,希望能够给大家带来帮助。


阅读量:747

点赞量:0

收藏量:0