ShareFlow和StateFlow中的生命周期管理

前言

这篇文章介绍Koltin中的Flow如何安全的收集数据流以及原理,这里的安全本质上指的是Flow需要管理自己的生命周期以适配Activity或者Fragment生命周期。其中一个case是,Activity退出了,Flow也应该退出收集数据流

危险行为和解法

下面是摘抄自使用 Kotlin Flow 构建数据流 "管道" 这篇文章中的代码。

kotlin 复制代码
class MessagesViewModel : ViewModel() {

    val userMessage: StateFlow<UiState> = getUserMessage().stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), UiState.empty())

}

class MessagesActivity : AppCompatActivity() {

    val viewModel: MessagesViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {

 

            // ❌ 危险的操作

            lifecycleScope.launch {

                viewModel.userMessage.collect { messages ->

                    listAdapter.submitList(messages)

                }

            }

 

            // ❌ 危险的操作

            LifecycleCoroutineScope.launchWhenX {

                flow.collect { ... }

            }

    }

}
  • 第一个危险操作是flow的上游和下游(collect)均和Activity的生命周期一致了,上游和下游的关闭操作均在onDestory之后了
  • 第二个危险操作,只是在started时开始收集,上游在stop后还是会保持活跃状态

正常解法应该是

markdown 复制代码
lifecycleScope.launch {

    repeatOnLifecycle(Lifecycle.State.STARTED)

        viewModel.userMessages.collect { messages ->

            listAdapter.submitList(messages)

        }

    }

}
  • 上述代码实现的效果是,activity退出后台停止上游和下游,并在activity显示的(STARTED)开启上游和下游

原理

先看上游, 上游的生命周期是由stateIn管理

stateIn

kotlin 复制代码
public fun <T> Flow<T>.stateIn(

    scope: CoroutineScope,

    started: SharingStarted,

    initialValue: T

): StateFlow<T> {

    // stateFlow是用shareFlow实现的, replay = 1的shareFlow

    val config = configureSharing(1)

    val state = MutableStateFlow(initialValue)

    // config.upstream == this

    scope.launchSharing(config.context, config.upstream, state, started, initialValue)

    return state.asStateFlow()

}
kotlin 复制代码
// Launches sharing coroutine

private fun <T> CoroutineScope.launchSharing(

    context: CoroutineContext,

    upstream: Flow<T>,

    shared: MutableSharedFlow<T>,

    started: SharingStarted,

    initialValue: T

) {

    launch(context) { // the single coroutine to rule the sharing

        // Optimize common built-in started strategies

        when {

            started === SharingStarted.Eagerly -> {

                // collect immediately & forever

                // 帮你collect,所以该启动类型是上游不等下游collect,直接运行

                upstream.collect(shared)

            }

            started === SharingStarted.Lazily -> {

                // start collecting on the first subscriber - wait for it first

                // 这个是流的first{},是一个suspend函数,意义就是等待第一个订阅者(collect函数调用),再开始上游

                shared.subscriptionCount.first { it > 0 }

                upstream.collect(shared)

            }

            else -> {

                // other & custom strategies

                // StartedWhileSubscribed的核心逻辑,请看2

                started.command(shared.subscriptionCount)

                // 这里做了防抖

                    .distinctUntilChanged() // only changes in command have effect

                    .collectLatest { // cancels block on new emission

                        when (it) {

                            SharingCommand.START -> upstream.collect(shared) // can be cancelled

                            SharingCommand.STOP -> { /* just cancel and do nothing else */ }

                            SharingCommand.STOP_AND_RESET_REPLAY_CACHE -> {

                                if (initialValue === NO_VALUE) {

                                    shared.resetReplayCache() // regular shared flow -> reset cache

                                } else {

                                    shared.tryEmit(initialValue) // state flow -> reset to initial value

                                }

                            }

                        }

                    }

            }

        }

    }

}

SharingStarted.command

下面的代码是StartedWhileSubscribed.command

scss 复制代码
// 这里注意subscriptionCount是一个流,当对应的Flow订阅发生变化时,才会往下游emit

 override fun command(subscriptionCount: StateFlow<Int>): Flow<SharingCommand> = subscriptionCount

        .transformLatest { count ->

            if (count > 0) {

                // 订阅者数量大约0, 立即执行

                emit(SharingCommand.START)

            } else {

                // <0, 可以停止该流的emit了

                // 等待多少ms,停止

                delay(stopTimeout)

                // replayExpiration默认值时MAX_VALUE

                if (replayExpiration > 0) {

                    // 立即停止

                    emit(SharingCommand.STOP)

                    // 挂起

                    delay(replayExpiration)

                }

                emit(SharingCommand.STOP_AND_RESET_REPLAY_CACHE)

            }

        }

        // 不是start就不会往下游发了

        .dropWhile { it != SharingCommand.START } // don't emit any STOP/RESET_BUFFER to start with, only START

        .distinctUntilChanged() // just in case somebody forgets it, don't leak our multiple sending of START
  • command 函数是控制了整个上游的开关
  • SharingStarted.EagerlySharingStarted.Lazily, 他们都是在没有下游collect时,不会停止的,唯一区别是开始时机
  • 当activity恢复的时候,subscriptionCount(注意这是flow)就会,重新emit,此时emit(SharingCommand.START)就会得到执行

下面看来下游如何close的, 下游的close类似手动写job.close()

RepeatOnLifecycle.kt

kotlin 复制代码
public suspend fun Lifecycle.repeatOnLifecycle(

    // 1. 根据你传的state决定上游job在哪个生命周期停止和结束

    state: Lifecycle.State,

    block: suspend CoroutineScope.() -> Unit

) {

    require(state !== Lifecycle.State.INITIALIZED) {

        "repeatOnLifecycle cannot start work with the INITIALIZED lifecycle state."

    }



    if (currentState === Lifecycle.State.DESTROYED) {

        return

    }



    coroutineScope {

        withContext(Dispatchers.Main.immediate) {

            // Check the current state of the lifecycle as the previous check is not guaranteed

            // to be done on the main thread.

            if (currentState === Lifecycle.State.DESTROYED) return@withContext



            // Instance of the running repeating coroutine

            var launchedJob: Job? = null



            // Registered observer

            var observer: LifecycleEventObserver? = null



            try {

                // Suspend the coroutine until the lifecycle is destroyed or

                // the coroutine is cancelled

                suspendCancellableCoroutine<Unit> { cont ->



                    // startWorkEvent = ON_START, cancelWorkEvent = ON_STOP

                    val startWorkEvent = Lifecycle.Event.upTo(state)

                    val cancelWorkEvent = Lifecycle.Event.downFrom(state)

                    // lifecycly组件观察生命周期变化

                    observer = LifecycleEventObserver { _, event ->

                        // Activity ON_START

                        if (event == startWorkEvent) {

                            // Launch the repeating work preserving the calling context

                            //  重新执行block,这里的block就是外部collect {}

                            launchedJob = this@coroutineScope.launch(block = block)

                            return@LifecycleEventObserver

                        }

                        if (event == cancelWorkEvent) {

                            // ON_STOP job.cancel()

                            launchedJob?.cancel()

                            launchedJob = null

                        }

                        if (event == Lifecycle.Event.ON_DESTROY) {

                            cont.resume(Unit)

                        }

                    }

                    // 这里add了,在顶层lifecycleScope.launch上会remove掉

                    this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)

                }

            } finally {

                launchedJob?.cancel()

                observer?.let {

                    this@repeatOnLifecycle.removeObserver(it)

                }

            }

        }

    }

}
java 复制代码
@Nullable

public static Event upTo(@NonNull State state) {

    switch (state) {

        case CREATED:

            return ON_CREATE;

        case STARTED:

            return ON_START;

        case RESUMED:

            return ON_RESUME;

        default:

            return null;

    }

}
java 复制代码
@Nullable

public static Event downFrom(@NonNull State state) {

    switch (state) {

        case CREATED:

            return ON_DESTROY;

        case STARTED:

            return ON_STOP;

        case RESUMED:

            return ON_PAUSE;

        default:

            return null;

    }

}
相关推荐
数据猎手小k1 小时前
AndroidLab:一个系统化的Android代理框架,包含操作环境和可复现的基准测试,支持大型语言模型和多模态模型。
android·人工智能·机器学习·语言模型
你的小101 小时前
JavaWeb项目-----博客系统
android
风和先行2 小时前
adb 命令查看设备存储占用情况
android·adb
AaVictory.3 小时前
Android 开发 Java中 list实现 按照时间格式 yyyy-MM-dd HH:mm 顺序
android·java·list
似霰4 小时前
安卓智能指针sp、wp、RefBase浅析
android·c++·binder
大风起兮云飞扬丶4 小时前
Android——网络请求
android
干一行,爱一行4 小时前
android camera data -> surface 显示
android
断墨先生4 小时前
uniapp—android原生插件开发(3Android真机调试)
android·uni-app
无极程序员5 小时前
PHP常量
android·ide·android studio
萌面小侠Plus7 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机