Compose中的函数副作用

667 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第11天,点击查看活动详情

Compose中函数副作用

可组合项理应是没有任何附带效应的。但是,在我们的使用日常中,我们可能会希望在状态进行转变时 触发一些逻辑,或者是发生一些可组合项的重组。从能感知可组合项生命周期的受控环境中调用这些可组合项。接下来学习Compose中不同副作用的API

可组合项的生命周期

可组合项的生命周期比Activity和Fragment更简单,一般时进入组合,执行0或多次重组,退出组合

lifecycle-composition.png

  • Enter:进入组件树,首次显示
  • Composotoin : 重组刷新UI
  • Leave:可组合项从组件树移除,不在显示

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第一个参数接收一个Key,当Key发生变化,系统将取消现有的协程,并且在新的协程中启动新的挂起函数。这就是重启效应

@Composable
fun HomeScreen(){
    //首次进入可组合项时就会执行LaunchedEffect代码块中的代码
    LaunchedEffect(true){

    }
    //LaunchedEffect本身是一个可组合函数,我们可以通过移除和加入组件树来控制block代码块的执行
    //isError状态值为false的时候LaunchedEffect不会进入组件树
    if(isError.value){
        LaunchedEffect(Unit){
            Log.d("LaunchedEffect","LaunchedEffect--${focusState.value}")
        }
    }
    //用其他状态去控制代码块,当状态发生变化,就会去执行由当前这个状态带来的副作用代码
    val focusState = remember { mutableStateOf(false) }
    LaunchedEffect(focusState.value){
        Log.d("LaunchedEffect","LaunchedEffect--${focusState.value}")
    }
}

LaunchedEffect.png

可以看出focusState发生变化时,带来的结果就是LaunchedEffect中的代码块重新执行

rememberCoroutineScope 获取组合感知作用域,以便在可组合项外启动协程

我们使用LaunchedEffect启动的协程作用域,只能在该可组合作用域范围内使用,如果在该组合项范围之外启动一个协程,那就需要使用rememberCoroutineScope ,rememberCoroutineScope 是一个可组合函数,使用范围只能在可组合函数中,它会返回一个CoroutineScope,并且该协程作用域会绑定到调用的组合点,如果与之绑定的组合项被取消,协程作用域也将被取消,并且使用rememberCoroutineScope 创建的协程作用域不会跟随可组合项的重组而反复创建,并且默认执行在Dispatcher.Main上

@Composable
fun HomeScreen(){
    val scope = rememberCoroutineScope()
    val viewModel:LoginViewModel = viewModel()
    MarkButton("Login"){
        scope.launch(Dispatcher.IO){
            viewModel.login()
        }
    }
}

@Composable
fun MarkButton(
    buttonText:String,
	onClick:()->Unit
){
    Box(modifier = Modifier.clickable{
        onClick()
    }){
       Text(text = buttonText,style = TextStyle(color = white,fontsize = 19.sp)) 
    }
}

使用rememberCoroutineScope 创建的协程,它的启动范围实在MarkButton中,如果MarkButton被取消那么使用scope启动的协程作用域也会相应的被取消

DisposableEffect 需要清理的效应

DisposableEffect副作用实在需要的键发生变化或可组合项退出组合后进行清理的附带效应,我们可以使用次副作用来使我们的可组合函数能感知Acitivity的生命周期

@Composable
fun UseScreen(){
    val lifeCycleOwner = LocalLifecycleOwner.current
    DisposableEffect(lifeCycleOwner){
        val observer = LifecycleEventObserver{_,event ->
            when(event){
                Lifecycle.Event.ON_CREATE ->{
                    Log.d("LifecycleEventObserver","ON_CREATE")
                }
                Lifecycle.Event.ON_START ->{
                    Log.d("LifecycleEventObserver","ON_START")
                }
                Lifecycle.Event.ON_RESUME ->{
                    Log.d("LifecycleEventObserver","ON_RESUME")
                }
                Lifecycle.Event.ON_PAUSE ->{
                    Log.d("LifecycleEventObserver","ON_PAUSE")
                }
                Lifecycle.Event.ON_STOP ->{
                    Log.d("LifecycleEventObserver","ON_STOP")
                }
                Lifecycle.Event.ON_DESTROY ->{
                    Log.d("LifecycleEventObserver","ON_DESTROY")
                }
            }
        }
        lifeCycleOwner.lifecycle.addObserver(observer)
        onDispose {
            lifeCycleOwner.lifecycle.removeObserver(observer)
        }
    }
}

image.png