在 Jetpack Compose 中安全地使用数据流

1c0936efead2d90f05ad222cd2655dd3.jpeg

/   今日科技快讯   /

11月17日下午,暴雪中国官方微博发布公告称,各位暴雪游戏的国服玩家,我们很遗憾地通知大家,随着我们与网之易公司现有授权协议的到期,自2023年1月24日0点起,所有《魔兽世界》、《魔兽争霸III:重制版》、《星际争霸》系列,《炉石传说》、《风暴英雄》、《守望先锋》及《暗黑破坏神 III》国服游戏都将停止运营。《暗黑破坏神:不朽》的联合开发与发行则由两家公司另外的协议所涵盖。

/   前言   /

我们推荐以生命周期感知方式在 Android 上收集数据流。如果您正在用 Jetpack Compose 构建 Android 应用,请使用 collectAsStateWithLifecycle API 以生命周期感知方式从用户界面收集数据流。

使用界面状态 (https://developer.android.google.cn/topic/architecture/ui-layer#consume-ui-state)

借助 collectAsStateWithLifecycle,您可以在不需要应用资源时释放它们,例如当应用处于后台时。此类资源可能包括 Firebase 查询、位置或网络更新及数据库连接等,在不需要它们的情况下让其处于活跃状态会影响用户设备的运行健康状况。

请继续阅读本文,以详细了解此 API、以生命周期感知方式收集数据流的理由,以及此 API 与 collectAsState API 的差异。

/   collectAsStateWithLifecycle   /

collectAsStateWithLifecycle 是一个可组合函数,可从数据流中收集值,并以生命周期感知方式将最新值表示为 Compose State。每当数据流发出新值时,此 State 对象的值都会更新,从而让组合 (Composition) 中每个使用 State.value 的对象进行重新组合。

State (https://developer.android.google.cn/reference/kotlin/androidx/compose/runtime/State)

默认情况下,collectAsStateWithLifecycle 使用 Lifecycle.State.STARTED 从数据流中开始和结束收集值。这些动作会在生命周期 (Lifecycle) 移入和移出目标状态时发生。您可以通过 minActiveState 参数配置此生命周期状态。

b44fd3a177070188ede2d97ae1c04a17.png

默认情况下,当应用处于后台时 collectAsStateWithLifecycle 会取消收集数据流

Lifecycle.State.STARTED (https://developer.android.google.cn/reference/android/arch/lifecycle/Lifecycle.State#started)

以下代码片段展示了如何使用 collectAsStateWithLifecycle 来收集可组合函数中的 ViewModel 所公开的 StateFlow 的 uiState 字段:

/* Copyright 2022 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@OptIn(ExperimentalLifecycleComposeApi::class)
@Composable
fun AuthorRoute(
  onBackClick: () -> Unit,
  modifier: Modifier = Modifier,
  viewModel: AuthorViewModel = hiltViewModel()
) {
  val uiState: AuthorScreenUiState by viewModel.uiState.collectAsStateWithLifecycle()

  AuthorScreen(
    authorState = uiState.authorState,
    newsState = uiState.newsState,
    modifier = modifier,
    onBackClick = onBackClick,
    onFollowClick = viewModel::followAuthorToggle,
  )
}

每当 AuthorViewModel 的 uiState 发出新的 AuthorScreenUiState 值时,都会重新组合 AuthorRoute。有关 collectAsStateWithLifecycle 的更多用法,请参考 "Now in Android" 应用及相关迁移 PR。

  • AuthorViewModelhttps://github.com/android/nowinandroid/blob/main/feature-author/src/main/java/com/google/samples/apps/nowinandroid/feature/author/AuthorViewModel.kt

  • AuthorRoute  https://github.com/android/nowinandroid/blob/main/feature-author/src/main/java/com/google/samples/apps/nowinandroid/feature/author/AuthorScreen.kt

  • Now in Android  https://github.com/android/nowinandroid/search?q=collectAsStateWithLifecycle

  • 迁移 PR  https://github.com/android/nowinandroid/pull/166

如果您要在项目中使用 collectAsStateWithLifecycle API,请将 androidx.lifecycle.lifecycle-runtime-compose 工件添加到项目中。

/* Copyright 2022 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

// app/build.gradle file
dependencies {
    implementation "androidx.lifecycle:lifecycle-runtime-compose:2.6.0-alpha01"
}

注意: 这是一个尚处于 Alpha 版的全新 API,且该 API 还要求您使用 ExperimentalLifecycleComposeApi 注释。

  • 版本 2.6.0-alpha01https://developer.android.google.cn/jetpack/androidx/releases/lifecycle#version_26_2

  • ExperimentalLifecycleComposeApihttps://developer.android.google.cn/reference/kotlin/androidx/lifecycle/compose/ExperimentalLifecycleComposeApi

/   工作原理   /

collectAsStateWithLifecycle 在实现机制上使用了 repeatOnLifecycle API,我们也推荐大家在 Android 视图 (View) 系统中收集数据流的 API。

  • collectAsStateWithLifecycle 的实现机制 https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:lifecycle/lifecycle-runtime-compose/src/main/java/androidx/lifecycle/compose/FlowExt.kt;l=168

  • repeatOnLifecycle  https://developer.android.google.cn/reference/kotlin/androidx/lifecycle/package-summary#(androidx.lifecycle.Lifecycle).repeatOnLifecycle(androidx.lifecycle.Lifecycle.State,kotlin.coroutines.SuspendFunction1)

借助 collectAsStateWithLifecycle,您无需输入下方的样板代码,这些代码同样以生命周期感知的方式从可组合函数收集数据流:

/* Copyright 2022 Google LLC.        
   SPDX-License-Identifier: Apache-2.0 */
@Composable
fun AuthorRoute(...) {
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    val uiState by produceState<AuthorScreenUiState>(
        initialValue = viewModel.uiState.value
        key1 = lifecycle
        key2 = viewModel
    ) {
        lifecycle.repeatOnLifecycle(state = STARTED) {
            viewModel.uiState.collect { value = it }
        }
    }

    AuthorScreen(...)
}

/   在架构中收集数据流   /

应用架构中的类型不应该知道其他类型的实现细节。界面不应该知道 ViewModel 如何产生界面状态。如果界面在屏幕上不可见,则应停止收集数据流,以释放应用资源 (如果可行的话)。

界面可以通过使用 collectAsStateWithLifecycle 收集界面状态来帮助释放资源。ViewModel 可以通过以收集器感知的方式生成界面状态来完成相同的操作。如果没有收集器,例如当界面在屏幕上不可见时,则停止收集来自数据层的上游数据流。您可以在生成界面状态时使用 .stateIn(WhileSubscribed) 数据流 API 来执行此操作。如需了解更多信息,请观看 "Kotlin Flows 实战" 讲座的这一部分。如要测试以这种方法生成界面状态的 ViewModel,请查看测试指南。

cca3a08cdbfdae4f90b1eb1e299419aa.png

在界面层中,使用 collectAsStateWithLifecycle 收集界面状态,并在数据层公开响应式数据流时使用 .stateIn(WhileSubscribed) 生成界面状态。这样一来应用的其余部分便能在不需要的时候释放资源

.stateIn(WhileSubscribed) https://github.com/android/nowinandroid/blob/main/feature-author/src/main/java/com/google/samples/apps/nowinandroid/feature/author/AuthorViewModel.kt#L104

Kotlin Flows 实战 https://www.youtube.com/watch?v=fSB6_KE95bU&t=1009s

测试 StateFlow https://developer.android.google.cn/kotlin/flow/test#statein

数据流的使用者和生产者不需要知道彼此的实现方式。在具备多个环境、变体、代码库和功能的大型应用中找出实现细节是非常耗时的。更糟糕的是,依赖于实现细节的代码维护起来非常困难。

/   让资源在后台保持活跃状态   /

Android 应用可以在海量 Android 设备上运行。但遗憾的是,所有设备和用户拥有的资源都是有限的,因此应用通常在受限环境中运行。运行 Android 应用时,有一些重要因素会影响用户体验和设备系统健康:

  • CPU 使用: 在所有设备组件中,CPU 的耗电量最高。而电池续航时间一直是用户关注的重点,因此如果发生 CPU 滥用的情况,用户可能会卸载您的应用;

  • 流量消耗: 在未连接 Wi-Fi 时减少应用的网络流量,可以帮助用户节省流量费用;

  • 内存用量: 应用对内存的使用方式也会对设备的整体稳定性和性能产生非常大的影响。

如果 Android 开发者想满足用户的需求、确保设备系统健康,或 "为数十亿用户打造产品",则应该根据其目标市场、设备或国家/地区的实际情况来优化上述这些因素。根据设备类型和设备上 Android 版本的不同,让不必要的资源保持活跃可能会产生负面影响。在界面层中使用 collectAsStateWithLifecycle 可以让层次结构的其余部分得以释放资源。

为数十亿用户打造产品 https://developer.android.google.cn/docs/quality-guidelines/build-for-billions

/   与collectAsState的差异   /

开发者们经常会问道: 如果 collectAsStateWithLifecycle 是从 Android 可组合函数中收集数据流最安全的方法,那现在为什么还需要 collectAsState API?为什么不将生命周期感知功能添加到 collectAsState 中,而是创建新的 API?

可组合函数的生命周期与 Compose 运行的平台无关。正如 "可组合项的生命周期" 页面中所述,可组合函数的实例进入组合,执行 0 次或多次重组,然后离开组合:https://developer.android.google.cn/jetpack/compose/lifecycle

8fea9199328774eeb92a0f878fc16c63.png

组合中可组合函数实例的生命周期

collectAsState API 遵循组合的生命周期。此 API 在可组合项进入组合时开始收集数据流,并在可组合项离开组合时停止收集。collectAsState 是用于收集数据流且与平台无关的 API。

但是,在 Android 应用中使用 Compose 时,Android 生命周期也会对资源的管理方式产生非常大的影响。即使 Compose 在 Android 应用处于后台时停止重组,collectAsState 也会继续收集数据流。这使得层次结构的其余部分无法释放资源。

collectAsState 和 collectAsStateWithLifecycle 在 Compose 中各有用途。后者用于开发 Android 应用,前者用于在其他平台进行开发。

从 collectAsState 迁移到 collectAsStateWithLifecycle 非常容易:

/* Copyright 2022 Google LLC.  
   SPDX-License-Identifier: Apache-2.0 */

@Composable
fun AuthorRoute(...) {
    val lifecycle = LocalLifecycleOwner.current.lifecycle
    val uiState by produceState<AuthorScreenUiState>(
        initialValue = viewModel.uiState.value
        key1 = lifecycle
        key2 = viewModel
    ) {
        lifecycle.repeatOnLifecycle(state = STARTED) {
            viewModel.uiState.collect { value = it }
        }
    }

    AuthorScreen(...)
}

推荐大家以生命周期感知方式在 Android 上收集数据流,这样做可以使应用的其他部分在需要时释放资源。

如果您正在使用 Jetpack Compose 构建 Android 应用,请使用 collectAsStateWithLifecycle 可组合函数来执行此操作。

推荐阅读:

我的新书,《第一行代码 第3版》已出版!

Kotlin Flow响应式编程,基础知识入门

Android 13 返回导航大变更

欢迎关注我的公众号

学习技术或投稿

67afd9f16589203aff8366c1e8f3792a.png

e891c3311465c97af31aa8d02c6020e2.jpeg

长按上图,识别图中二维码即可关注

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值