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;

    }

}
相关推荐
雨白7 小时前
Jetpack系列(二):Lifecycle与LiveData结合,打造响应式UI
android·android jetpack
kk爱闹8 小时前
【挑战14天学完python和pytorch】- day01
android·pytorch·python
每次的天空10 小时前
Android-自定义View的实战学习总结
android·学习·kotlin·音视频
恋猫de小郭10 小时前
Flutter Widget Preview 功能已合并到 master,提前在体验毛坯的预览支持
android·flutter·ios
断剑重铸之日11 小时前
Android自定义相机开发(类似OCR扫描相机)
android
随心最为安11 小时前
Android Library Maven 发布完整流程指南
android
岁月玲珑12 小时前
【使用Android Studio调试手机app时候手机老掉线问题】
android·ide·android studio
还鮟16 小时前
CTF Web的数组巧用
android
小蜜蜂嗡嗡17 小时前
Android Studio flutter项目运行、打包时间太长
android·flutter·android studio
aqi0017 小时前
FFmpeg开发笔记(七十一)使用国产的QPlayer2实现双播放器观看视频
android·ffmpeg·音视频·流媒体