Jetpack Compose 可组合项生命周期及其常见处理Effect副作用API_jetpack compose return@

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

上图描述了组合中可组合项的生命周期:进入组合,执行 0 次或多次重组,然后退出组合。在可组合项执行过程中,影响操作结果的可以认为是一种副作用,如在Composable中执行网络请求、Dialog弹窗、弹Toast、页面跳转等,不管重组多少次,有些操作只需要执行一次即可,如果多次执行就会出现意想不到的结果。针对上述情况,Compose 有一系列专门的副作用 API 来处理。

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) }
}

LaunchedEffect 进入可组合项时,它会启动一个协程。当LaunchedEffect 退出组合时,协程也将会取消。LaunchedEffect() 中可以传入一个key,如果重组时key发生改变,现有协程会被取消,并在新的协程中启动新的挂起函数。

注:如果是LaunchedEffect(true) 或 LaunchedEffect(Unit), 可以保证LaunchedEffect()中的key永远不发生变化,从而保证后面的lambda不参与重组。

使用示例:

@Composable
fun MyScreen(
    state: UiState<List<Movie>>,
    scaffoldState: ScaffoldState = rememberScaffoldState()
) {

    // 第一次组合时如果state.hasError为true,那么在LaunchedEffect开启协程并显示Snackbar;当重组state.hasError变为false后,协程结束,Snackbar也会消失
    if (state.hasError) {

        // 如果重组时scaffoldState.snackbarHostState发生变化,协程会被取消并重新创建并执行
        LaunchedEffect(scaffoldState.snackbarHostState) {
            // 展示Snackbar
            scaffoldState.snackbarHostState.showSnackbar(
                message = "Error message",
                actionLabel = "Retry message"
            )
        }
    }

    Scaffold(scaffoldState = scaffoldState) {
        /* ... */
    }
}

state.hasError状态为 true时,会触发LaunchedEffect() 通过协程处理副作用;如果重组时state.hasError变成 false,协程会被取消,Snackbar也会消失。

rememberCoroutineScope()

LaunchedEffect 是由@Composable 标记的可组合函数,因此只能在其他可组合函数中使用(类似协程中的suspend)。当需要在可组合项外启动协程时(如onClick中),可以使用rememberCoroutineScope ,源码如下:

@Composable
inline fun rememberCoroutineScope(
    getContext: @DisallowComposableCalls () -> CoroutineContext = { EmptyCoroutineContext }
): CoroutineScope {
    val composer = currentComposer
    val wrapper = remember {
        CompositionScopedCoroutineScopeCanceller(
            createCompositionCoroutineScope(getContext(), composer)
        )
    }
    return wrapper.coroutineScope
}

rememberCoroutineScope() 返回 CoroutineScope 协程作用域,可以在可组合项外启动协程,该 CoroutineScope 绑定到调用它的组合点。调用退出组合后,作用域将取消

使用示例:

@Composable
fun ComposeEffect() {
    var txt by remember { mutableStateOf("") }
    //rememberCoroutineScope在非重组作用域启动协程任务
    val coroutineScope = rememberCoroutineScope()

    Column(
        modifier = Modifier.fillMaxSize()wrapContentSize(Alignment.Center)
    ) {
        Text(text = txt)
        Button(onClick = {
            //在onClick中启动了协程
            coroutineScope.launch {
                delay(3000)
                txt = "修改文案"
            }
        }) {
            Text(text = "rememberCoroutineScope")
        }
    }
}

点击Button后,启动协程,在协程中延迟3s重组并刷新Text文案。

DisposableEffect
@Composable
@NonRestartableComposable
fun DisposableEffect(
    key1: Any?,
    effect: DisposableEffectScope.() -> DisposableEffectResult
) {
    remember(key1) { DisposableEffectImpl(effect) }
}

对于需要在键发生变化或可组合项退出组合后进行清理的副作用,可以使用 DisposableEffect。如果 DisposableEffect 中的key发生变化,那么就会执行onDispose()资源释放等操作,并重新执行DisposableEffect。

DisposableEffect中强制以onDispose进行收尾,不加就会编译报错。因此DisposableEffect适合有资源需要收尾的场景下。

使用示例:

@Composable
fun HomeScreen(
    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
    onStart: () -> Unit, // Send the 'started' analytics event
    onStop: () -> Unit // Send the 'stopped' analytics event
) {
    // Safely update the current lambdas when a new one is provided
    val currentOnStart by rememberUpdatedState(onStart)
    val currentOnStop by rememberUpdatedState(onStop)

    // If `lifecycleOwner` changes, dispose and reset the effect
    DisposableEffect(lifecycleOwner) {
        // Create an observer that triggers our remembered callbacks
        // for sending analytics events
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START) {
                currentOnStart()
            } else if (event == Lifecycle.Event.ON_STOP) {
                currentOnStop()
            }
        }

        // Add the observer to the lifecycle
        lifecycleOwner.lifecycle.addObserver(observer)

        // When the effect leaves the Composition, remove the observer
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }

    /* Home screen content */
}

LaunchedEffect 内部开启了协程,并在退出组合时取消协程,DisposableEffect + rememberCoroutineScope 的方式可以模拟 LaunchedEffect,如下:

//LaunchedEffect
LaunchedEffect(key1 = Unit) {
}

//DisposableEffect + rememberCoroutineScope
val scope = rememberCoroutineScope()
DisposableEffect(key1 = Unit) {
    val job = scope.launch { }
    onDispose {
        job.cancel()
    }
}

img
img

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

需要这份系统化的资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值