Android 构建可管理生命周期的应用(二)

在前面第一篇有聊过

  • LifecycleOwner 是一个接口,表示某个类拥有一个 Lifecycle

  • LifecycleRegistryLifecycle 类的一个具体实现,它提供了管理生命周期状态的方法

  • 还举例传统分式中 ViewModelLiveData 如何跟随 FragmentActivity 中的Lifecycle生命周期工作的

今天我们聊,在现代应用中,另一个组件协程,如何如何跟随 FragmentActivity 中的Lifecycle生命周期工作的

先来看一下典型的例子

kotlin 复制代码
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    viewLifecycleOwner.lifecycleScope.launch {
        repeatOnLifecycle(Lifecycle.State.STARTED) {
            someFlow.collect {
                // 更新 UI
            }
        }
    }
}

背后逻辑就是:

  • 页面可见且处于前台时(STARTED)才开始 collect
  • 页面不可见(变为 STOPPED)时自动取消 collect
  • 页面销毁时自动清理资源

lifecycleScope

kotlin 复制代码
public interface LifecycleOwner {
public val lifecycle: Lifecycle
}

public val LifecycleOwner.lifecycleScope: LifecycleCoroutineScope
    get() = lifecycle.coroutineScope

跟传统方式比较,就是扩展了一个属性*lifecycleScope*

内部是调用 lifecycle.coroutineScope

简单讲一下kotlin 扩展属性,lifecycle.coroutineScope **这段代码可以理解成 lifecycle 该类型的一个对象,访问了它的coroutineScope属性。下面继续看一下源码实现逻辑。

kotlin 复制代码
public val Lifecycle.coroutineScope: LifecycleCoroutineScope
    get() {
        while (true) {
            val existing = internalScopeRef.get() as LifecycleCoroutineScopeImpl?
            if (existing != null) {
                return existing
            }
            val newScope =
                LifecycleCoroutineScopeImpl(this, SupervisorJob() + Dispatchers.Main.immediate)
            if (internalScopeRef.compareAndSet(null, newScope)) {
                newScope.register()
                return newScope
            }
        }
    }

这个协程作用域内部交给了LifecycleCoroutineScopeImpl 它去实现了

kotlin 复制代码
internal class LifecycleCoroutineScopeImpl(
    override val lifecycle: Lifecycle,
    override val coroutineContext: CoroutineContext
) : LifecycleCoroutineScope(), LifecycleEventObserver {
    init {
        if (lifecycle.currentState == Lifecycle.State.DESTROYED) {
            coroutineContext.cancel()
        }
    }

    fun register() {
        launch(Dispatchers.Main.immediate) {
if (lifecycle.currentState >= Lifecycle.State.INITIALIZED) {
                lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)
            } else {
                coroutineContext.cancel()
            }
        }
}

    override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
        if (lifecycle.currentState <= Lifecycle.State.DESTROYED) {
            lifecycle.removeObserver(this)
            coroutineContext.cancel()
        }
    }
}

内部先是注册了生命周期

lifecycle.addObserver(this@LifecycleCoroutineScopeImpl)

然后监听生命周期的变化onStateChanged 在页面销毁的时候,解除绑定,协程取消。

lifecycle.currentState <= Lifecycle.State.DESTROYED

所以最外层的协程viewLifecycleOwner.lifecycleScope.launch的生命周期结束是在 Lifecycle.State.DESTROYED 也就是fragment销毁之后

repeatOnLifecycle

kotlin 复制代码
public suspend fun LifecycleOwner.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
): Unit = lifecycle.repeatOnLifecycle(state, block)

继续看一下第二次的协程方法,这是一个拓展函数,一样是来自LifecycleOwner,内部实现依旧是交给lifecyclelifecycle.repeatOnLifecycle也是一个拓展函数,分析一下源码实现逻辑

kotlin 复制代码
public suspend fun Lifecycle.repeatOnLifecycle(
    state: Lifecycle.State,
    block: suspend CoroutineScope.() -> Unit
) {
    ...
    coroutineScope {
withContext(Dispatchers.Main.immediate) {
            if (currentState === Lifecycle.State.DESTROYED) return@withContext
            var launchedJob: Job? = null
            var observer: LifecycleEventObserver? = null
            try {
                suspendCancellableCoroutine<Unit> { cont ->
                    val startWorkEvent = Lifecycle.Event.upTo(state)
                    val cancelWorkEvent = Lifecycle.Event.downFrom(state)
                    val mutex = Mutex()
                    observer = LifecycleEventObserver { _, event ->
if (event == startWorkEvent) {
                            launchedJob =
                                this@coroutineScope.launch {
                                    mutex.withLock { coroutineScope { block() } }
}
return@LifecycleEventObserver
                        }
                        if (event == cancelWorkEvent) {
                            launchedJob?.cancel()
                            launchedJob = null
                        }
                        if (event == Lifecycle.Event.ON_DESTROY) {
                            cont.resume(Unit)
                        }
                    }
this@repeatOnLifecycle.addObserver(observer as LifecycleEventObserver)
                }
} finally {
                launchedJob?.cancel()
                observer?.let { this@repeatOnLifecycle.removeObserver(it) }
}
        }
}
}

suspendCancellableCoroutine 这是一个挂起函数,它需要我们手动去恢复 cont.resume (Unit)就是恢复逻辑,内部实现是注册了生命周期观察者 addObserver(observer as LifecycleEventObserver)

继续把代码简化一下,看看LifecycleEventObserver生命周期变化相关逻辑,

如果我们设置的是repeatOnLifecycle(Lifecycle.State.STARTED)

ini 复制代码
// LifecycleEventObserver
if (STARTED == startWorkEvent) {
    launchedJob =
        this@coroutineScope.launch {
            mutex.withLock { coroutineScope { block() } }
}
return@LifecycleEventObserver
}
if (ON_STOP == cancelWorkEvent) {
    launchedJob?.cancel()
    launchedJob = null
}
if (event == Lifecycle.Event.ON_DESTROY) {
    cont.resume(Unit)
}

也就是页面可见的时候,执行协成代码块,也就是

arduino 复制代码
someFlow.collect {
    // 更新 UI
}

页面不可见的时候,取消协程代码块执行

页面销毁的时候*ON_DESTROY,手动恢复协程cont. resume*(Unit),相当于协程结束了

所以,someFlow.collect更新UI的逻辑,在*ON_STOP会被取消了,跟最外层的协程逻辑是有差异的,最外层是更长的生命周期,它持续到 ON_DESTROY*

repeatOnLifecycle(Lifecycle.State.STARTED)很适合UI更新

相关推荐
alexhilton20 小时前
Android的Agent优先时代:构建时vs运行时
android·kotlin·android jetpack
Cutecat_21 小时前
视频字幕处理工具横向:提取模式 vs 编辑模式,该如何选择
android·前端·ios·语音识别
2601_961765291 天前
【分享】PlayerPro媒体音乐播放器 完整专业版
android·媒体
JohnnyDeng941 天前
【Android】Android 包体积优化:R8/ProGuard 深度配置全攻略
android·性能优化·kotlin·jetpack
故渊at1 天前
第九板块:Android 多媒体体系 | 第二十四篇:Camera Service 与 HAL3 成像流水线
android·camera·多媒体体系·hal3
Jinkxs1 天前
Python基础 - 初识内置函数 Python自带的便捷工具
android·java·python
私人珍藏库1 天前
【Android】VLLO-韩国热门手机剪辑APP
android·app·工具·软件·多功能
Cloud_Shy6181 天前
解读《Effective Python 3rd Edition》:从练气到老魔(第六章 Item 40 - 43)
android·开发语言·人工智能·笔记·python·学习方法
AFinalStone1 天前
Android12 U盘插拔链路源码全解析(五):Framework层(下) StorageManagerService
android·frameworks
林九生1 天前
【实用技巧】MySQL 绿色版一键路径更新脚本详解 —— update_path.bat 深度解析
android·数据库·mysql