赞
踩
通过这一趴,你将学习到
LaunchEffect
、rememberUpdateState
、DisposeableEffect
、produceState
和derivedStateOf
。rememberCoroutineScope
API在可组合项中创建协程并调用挂起函数。获取代码
git clone https://github.com/googlecodelabs/android-compose-codelabs
请使用 AdvancedStateAndSideEffectsCodelab 项目。
该项目在多个 git 分支中构建而成:
预期的结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pVnZMTq3-1690449106121)(https://p9-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/443824e0e98a4a6eb3aacc62ab2a6d49~tplv-k3u1fbpfcp-zoom-in-crop-mark:3024:0:0:0.image?)]
界面状态生成是指以下过程:应用访问数据层、应用业务规则,以及公开要从界面取用的界面状态。
一般而言,最好使用Kotlin的StateFlow
生成界面可以取用该状态。
如果生成界面状态,请按以下步骤操作:
home/MainViewModel.kt
MutableStateFlow
的私有_suggestedDestinations
变量,用于表示推荐目的地列表,并将空列表设置为起始值。private val _suggestedDestinations = MutableStateFlow<List<ExplorModel>>(emptyList())
suggestedDestinations
,类型为StateFlow
。这是可从界面取用的公开只读变量。建议您公开只读变量,并在内部使用可变变量。这样做可确保界面状态无法修改,除非通过ViewModel
使其成为单一可信来源。扩展函数asStateFlow
会将可变流转换位不可变流。private val _suggestedDestinations = MutableStateFlow<List<ExploreModel>>(emptyList())
val suggestedDestinations: StateFlow<List<ExploreModel>> = _suggestedDestinations.asStateFlow()
ViewModel
的init块中,添加来自destinationsRepository
的调用,以便从数据层获取目的。private val _suggestedDestinations = MutableStateFlow<List<ExploreModel>>(emptyList())
val suggestedDestinations: StateFlow<List<ExploreModel>> = _suggestedDestinations.asStateFlow()
init {
_suggestedDestinations.value = destinationsRepository.destinations
}
_suggestedDestinations
使用情况,以便可通过来自界面的事件正确更新该变量。这样就完成第一步了!现在,ViewModel
能够生成界面状态。
航班目的地列表仍然为空。在上一步中,您在MainViewModel
中生成了界面状态。现在,您将使用要在界面中显示并由MainViewModel
公开的界面状态。
打开home/CreaneHome.kt
文件并查看CraneHomeContent
可组合项。
被分配给一个记住的空列表的suggestedDestinations
的定义上有一条TODO注释。这就是屏幕上显示的内容:一个空列表!在此步骤中,我们将解决该问题,并显示MainViewModel
公开的推荐目的地。
打开home/MainViewModel.kt
并查看suggestedDestinations
StateFlow,该StateFlow初始化为destinationsResponsitory.destinations
,并且会在调用updatePeople
或toDestinationChanged
函数时得到更新。
您希望每当有新项被发送到suggestedDestinations
数据流时CraneHomeContent
可组合项中的界面都会更新。您可以使用collectAsStateWithLifecycle()
函数。collectAsStateWithLifecycle()
会以生命周期感知型方式从StateFlow
手机值并通过Compose的State
API表示最新值。这样会使读取该状态值的Compose代码在发出新项时重组。
如需开始使用collectAsStateWithLifecycle
API,请先在app/build.gradle
中添加以下依赖项。变量liftcycle_version
已在项目中使用适当版本进行定义。
dependencies {
implementation "androidx.lifecycle:lifecycle-runtime-compose:$lifecycle_version"
}
返回CraneHomeContent
可组合项,并将分配suggestedDestinations
的代码行替换为ViewModel
的suggestedDestinations
属性上的collectAsStateWithLifecycle
调用:
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@Composable
fun CraneHomeContent(
onExploreItemClicked: OnExploreItemClieked,
openDrawer: () -> Unit,
modifier: Modifier = Modifier,
viewModel: MianViewModel = viewModel(),
) {
val suggestedDestinations by viewModel.suggestedDestinations.collectAsStateWithLifecycle()
// ...
}
如果您运行应用,您会看到目的地列表已填充,并且每当您点按旅行人数时,目的地都会发生变化。
Compose还为最热门的基于数据流的Android解决方案提供了API:
在该项目中,有一个目前未使用的home/LandingScreen.kt
文件。我们想要向应用添加一个着陆屏幕,它有可能会用于在后台加载需要的所有数据。
着陆屏幕将占据整个屏幕,并在屏幕中间显示应用的Logo。理想情况下,我们会显示该屏幕,在所有数据加载完毕之后,我们会通知调用方可以使用onTimeout
回调关闭着陆屏幕。
建议使用Kotlin协程在Android中执行异步操作。应用在启动时通常会使用协程在后台加载内容。Jetpack Compose提供了可让您在界面层中安全使用协议的API。由于此应用不与后端进行通信,因此我们将使用协程的delay
函数来模拟在后台加载内容。
Compose中的附带效应是指发生在可组合函数作用域之外的应用状态的变化。 例如,当用户点按一个按钮时打开一个新屏幕,或者在应用未连接到互联网时显示一条消息。
Compose中的附带效应是指发生在可组合函数作用域之外的应用状态的变化。将状态更改为显示/隐藏着陆屏幕的操作将发生在onTimeout
回调中,由于在调用onTimeout
之前我们需要先使用协程加载内容,因此转台变化必须发生在协程的上下文中!
如需从可组合项内安全地调用挂起函数,请使用LaunchedEffect
API,该API会在Compose中出发协程作用域的附带效应。
当LaunchedEffect
进入组合时,它会启动一个协程,并将代码块作为参数传递。如果LaunchedEffect
退出组合,协程将取消。
虽然接下来的代码不正确,但让我们看看如何使用此API,并探讨为什么下面的代码是错误的。我们将在此步骤后面调用LandingScreen
可组合项。
// home/LandingScreen.kt file import androidx.compose.runtime.LaunchedEffect import kotlinx.coroutines.delay @Composable fun LandingScreen(onTimeout: () -> Unit, modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { // Start a side effect to load things in the background // and call onTimeout() when finished. // Passing onTimeout as a parameter to LaunchedEffect // is wrong! Don't do this. We'll improve this code in a sec. LaunchedEffect(onTimeout) { delay(SplashWaitTime) // Sumulates loading things onTimeout() } Image(painterResource(id = R.drawable.ic_crane_drawer), contentDescription = null } }
某些附带效应API(如LaunchedEffect
)会将可变数量的键作为参数,用于在其中一个键发生更改时重新开始效应。我们不希望在此可组合函数的调用方传递不同的onTimeout
lambda值时重启LaunchedEffect
。这会让delay
在此启动,使得我们无法满足相关要求。
接下来,我们解决这个问题。如需在此组合项的生命周期内触发一次附带效应,请将常量作为键,例如LaunchedEffect(Unit) { ... }
。不过,现在又有一个问题。
如果onTimeout
在附带效应正在进行时发生变化,效应结束时不一定会调用最后一个onTimeout
。如需保证调用最后一个onTimeout
,请使用rememberUpdatedState
API记住onTimeout
。此API会捕获并更新最新值:
// home/LandingScreen.kt file import androidx.cpomose.runtime.getValue import androdix.compose.runtime.rememberUpdatedState import kotlinx.coroutines.delay @Composable fun Landing(onTimeout:() -> Unit, modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { // This will always refer to the latest onTimeout function that // LandingScreen was recomposed with val currentOnTimeout by rememberUpdatedState(onTimeout) // Create an effect that matches the lifecycle of LandingScreen. // If LandingScreen recomposes or onTimeout change, // the delay should't start again. LaunchedEffect(Unit) { delay(SplashWaitTime) currentOnTimeout() } Image(painterResource(id = R.drawable.ic_crane_drawer), contentDescription = null) } }
当长期存在的lambda或表达式引用在组合期间计算的参数或值时,您应使用rememberUpdatedState
,这在LaunchedEffect
时可能很常见。
现在,我们需要在应用打开后显示着陆屏幕。打开home/MainActivity.kt
文件,并查看首次调用的MainScreen
可组合项。
在MainScreen
可组合项中,我们只需添加一种内部状态,用来跟踪是否显示着陆屏幕:
// home/MianActivity.kt file import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.remember import androidx.compose.runtime.setValue @Composable private fun MainScreen(onEcploreItemClicked: OnExploreItemClicked) { Surface(color = MaterialTheme.colors.primary) { var showLandingScreen by remember { mutableStateOf(true) } if (showLoadingScreen) { LandingScreen(onTimeout = { showLandingScreen = false }) } else { CraneHome(onExploreItemClieked = onExploreItemClicked) } } }
如果您现在运行应用,您应该会看到LandingScreen
出现并在2秒后消失。
如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。
如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。
一、面试合集
二、源码解析合集
三、开源框架合集
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。