LaunchedEffect 简介
LaunchedEffect: run suspend functions in the scope of a composable
LaunchedEffectis executed once when entered inside the composition. And it is canceled when leaving the composition.LaunchedEffectcancels/re-launch whenKeysstate changesLaunchedEffectmust have at least onekeyLaunchedEffectScope’s Dispatcher is Main.
当 LaunchedEffect 进入组合时,它会启动一个协程,并将代码块作为参数传递。如果 LaunchedEffect 退出组合,协程将取消。
LaunchedEffect 源码如下
@Composable
@NonRestartableComposable
@OptIn(InternalComposeApi::class)
fun LaunchedEffect(
key1: Any?,
block: suspend CoroutineScope.() -> Unit
) {
val applyContext = currentComposer.applyCoroutineContext
remember(key1) { LaunchedEffectImpl(applyContext, block) }
}
internal class LaunchedEffectImpl(
parentCoroutineContext: CoroutineContext,
private val task: suspend CoroutineScope.() -> Unit
) : RememberObserver {
private val scope = CoroutineScope(parentCoroutineContext)
private var job: Job? = null
override fun onRemembered() {
job?.cancel("Old job was still running!")
job = scope.launch(block = task)
}
override fun onForgotten() {
job?.cancel()
job = null
}
override fun onAbandoned() {
job?.cancel()
job = null
}
}
从源码可以看出我们传入的希望在协程中执行的 block,确实也是通过创建一个
CoroutineScope,最终通过launch{}函数执行的。通过
remember{}与LaunchedEffectImpl的配合,实现了当 key1 参数的值发生变化时,上一个 job 取消,然后重新执行 block
如需在此 Compose 函数的生命周期内仅触发一次附带效应,请将常量用作键,例如 LaunchedEffect(true) { ... }
SideEffect
Running non-suspend functions in every recomposition
SideEffect是LaunchedEffect的一种,功能相似- 每次 Compose 函数重组都会执行
SideEffectblock 中不能运行suspend函数
官方文档的解释是:将 Compose 状态发布为非 Compose 代码
@Composable
fun test() {
var timer by remember { mutableStateOf(0) }
Box(modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center) {
Text("Time $timer")
}
SideEffect {
Thread.sleep(1000)
timer++
}
}
rememberUpdatedState
有时候需要在LaunchedEffect中使用最新的参数值,但是又不想重新启动LaunchedEffect,因为LaunchedEffect中包含了重量级的操作,重新启动会浪费资源,此时就需要用到rememberUpdatedState。
rememberUpdatedState的作用是给某个参数创建一个引用,并保证其值被使用时是最新值。
rememberCoroutineScope
用于在 Compose 函数中创建协程 coroutine
- rememberCoroutineScope 返回的 coroutineScope 会和其调用点的生命周期保持一致,当调用点所在的 Composition 退出时,该 coroutineScope 会被取消
rememberSaveable 状态容器
rememberSaveable 与 remember 都是存储功能,但 rememberSaveable 存储的值在 activity 和进程重新创建后会继续保存
对于可以存储在 Bundle 内的对象,rememberSaveable 可以做所有这些工作,无需额外操作
需要告知 rememberSaveable 如何使用 Saver 保存和恢复此类的实例
Saver 描述了如何将对象转换为 Saveable 的内容,Saver 需要替换两个函数:
save- 将原始值转换为可保存的值restore- 将保存的值转换为原始数据
DisposableEffect
官方文档解释:
For side effects that need to be cleaned up after the keys change or if the composable leaves the Composition, use
DisposableEffect. If theDisposableEffectkeys change, the composable needs to dispose (do the cleanup for) its current effect, and reset by calling the effect again.中文版:
对于需要在键发生变化或可组合项退出组合后进行清理的附带效应,请使用
DisposableEffect。如果
DisposableEffect键发生变化,可组合项需要处理(执行清理操作)其当前效应,并通过再次调用效应进行重置。
import androidx.compose.runtime.DisposableEffect
import androidx.compose.ui.platform.LocalLifecycleOwner
@Composable
fun rememberMapViewWithLifecycle(): MapView {
val context = LocalContext.current
val mapView = remember {
MapView(context).apply {
id = R.id.map
}
}
val lifecycle = LocalLifecycleOwner.current.lifecycle
DisposableEffect(key1 = lifecycle, key2 = mapView) {
// Make MapView follow the current lifecycle
val lifecycleObserver = getMapLifecycleObserver(mapView)
lifecycle.addObserver(lifecycleObserver)
onDispose {
lifecycle.removeObserver(lifecycleObserver)
}
}
return mapView
}
与 LaunchedEffect 相似,key 值变化的时(多个 key 值任一变化),会执行 DisposableEffect
- 先执行
DisposableEffect的onDispose - 再执行
DisposEffect代码块
ProduceState
普通对象数据改变无法触发 Compose 函数更新
通过 ProduceState 修饰后,成为有状态数据,数据变化会触发 Compose 函数刷新
@Composable
fun JustLaunchEffect() {
var timer by remember { mutableStateOf(0) }
LaunchedEffect(key1 = Unit) {
delay(1000)
timer++
}
}
@Composable
fun JustProduceState() {
val timer by produceState(initialValue = 0) {
delay(1000)
value++
}
}
以上两段代码效果等同
MutableState 与 ProduceState 区别:
ProduceState接收一个 lambda 表达式作为函数体,能将这些入参经过一些操作后生成 State 类型变量并返回
ProduceState 使用
- 创建时定义初始化值
- 给
value赋值
derivedStateOf
当您想要的某个 Compose State 衍生自另一个 State 时,会使用 derivedStateOf;
使用此函数可保证仅当计算中使用的状态之一发生变化时才会进行计算。
举例:
@Composable
fun test() {
var a by remember {
mutableStateOf(1)
}
val b = a > 0
if (b) {
Text("b is true")
}
}
// 此处 b 不会触发 Compose 函数的重组
修改后
@Composable
fun test() {
var a by remember {
mutableStateOf(1)
}
val b by remember {
derivedStateOf {
a > 0
}
}
if (b) {
Text("b is true")
}
}
文章参考: