Compose 副作用

34 阅读1分钟

LaunchedEffect - 协程副作用

/**

  • LaunchedEffect - 用于在Composable中启动协程

    1. 当LaunchedEffect进入组合时启动协程
    1. 当LaunchedEffect退出组合或key变化时取消协程
    1. 协程在Compose的协程作用域中运行
    1. 适合执行挂起函数:动画、网络请求等
  • @param key: 当key变化时,重启协程

  • @param block: 协程代码块 */ @Composable fun LaunchedEffectSection(counter: Int, text: String) { var timerValue by remember { mutableStateOf(0) } var apiData by remember { mutableStateOf("") } var error by remember { mutableStateOf(false) }

    Card( modifier = Modifier.fillMaxWidth(), elevation = 4.dp ) { Column( modifier = Modifier.padding(16.dp) ) { Text( text = "1. LaunchedEffect 演示", style = MaterialTheme.typography.h6, fontWeight = FontWeight.Bold )

         // 示例1: 基本计时器(key为Unit,只启动一次)
         LaunchedEffect(Unit) {
             // 这个协程在组合期间持续运行
             while (isActive) {
                 delay(1000)
                 timerValue++
             }
         }
         
         Text("计时器: $timerValue 秒")
         
         Divider(modifier = Modifier.padding(vertical = 8.dp))
         
         // 示例2: 当counter变化时重启的协程
         LaunchedEffect(counter) {
             // 当counter变化时,这个协程会取消并重新启动
             if (counter > 0) {
                 // 模拟网络请求
                 delay(1500)
                 apiData = "从API获取的数据: Counter=$counter"
                 error = false
             }
         }
         
         Text("API数据: $apiData")
         
         Divider(modifier = Modifier.padding(vertical = 8.dp))
         
         // 示例3: 多个key(当text或counter变化时重启)
         LaunchedEffect(text, counter) {
             if (text.isNotEmpty()) {
                 // 模拟搜索请求
                 delay(800)
                 // 这里可以执行搜索逻辑
             }
         }
         
         // 示例4: 错误处理和状态管理
         LaunchedEffect(error) {
             if (error) {
                 // 错误恢复逻辑
                 delay(2000)
                 error = false
             }
         }
         
         // 示例5: 启动动画
         var animatedValue by remember { mutableStateOf(0f) }
         LaunchedEffect(counter) {
             // 启动动画
             animateFloatAsState(
                 targetValue = if (counter % 2 == 0) 1f else 0f,
                 animationSpec = tween(500)
             ).value
         }
         
         Spacer(modifier = Modifier.height(8.dp))
         
         // 使用LaunchedEffect处理事件
         var showSnackbar by remember { mutableStateOf(false) }
         var snackbarMessage by remember { mutableStateOf("") }
         
         LaunchedEffect(counter) {
             if (counter > 5) {
                 snackbarMessage = "计数器超过5!"
                 showSnackbar = true
             }
         }
         
         if (showSnackbar) {
             LaunchedEffect(showSnackbar) {
                 delay(2000)
                 showSnackbar = false
             }
             Text(
                 text = snackbarMessage,
                 color = MaterialTheme.colorScheme.error
             )
         }
     }
    

    } }

LaunchedEffect 使用场景总结:

  • 适合使用LaunchedEffect的场景:
    1. 一次性初始化操作
    1. 响应状态变化的副作用
    1. 动画控制
    1. 网络请求
    1. 定时任务
    1. 事件监听和响应 */

DisposableEffect - 资源管理副作用

/**

  • DisposableEffect - 用于管理需要清理的资源

  • 特点:

    1. 当DisposableEffect进入组合时执行onStart
    1. 当DisposableEffect退出组合或key变化时执行onDispose
    1. 必须包含onDispose子句
    1. 适合管理:事件监听、广播接收器、资源连接等
  • @param key: 当key变化时,先执行旧的onDispose,再执行新的onStart */ @Composable fun DisposableEffectSection() { var lifecycleState by remember { mutableStateOf("") } var eventCount by remember { mutableStateOf(0) }

    Card( modifier = Modifier.fillMaxWidth(), elevation = 4.dp ) { Column( modifier = Modifier.padding(16.dp) ) { Text( text = "2. DisposableEffect 演示", style = MaterialTheme.typography.h6, fontWeight = FontWeight.Bold )

         // 示例1: 监听生命周期
         val lifecycleOwner = LocalLifecycleOwner.current
         
         DisposableEffect(lifecycleOwner) {
             // 创建观察者
             val observer = LifecycleEventObserver { _, event ->
                 lifecycleState = event.name
                 eventCount++
             }
             
             // 注册观察者
             lifecycleOwner.lifecycle.addObserver(observer)
             
             // 清理函数(必须的)
             onDispose {
                 lifecycleOwner.lifecycle.removeObserver(observer)
                 println("Lifecycle observer removed")
             }
         }
         
         Text("当前生命周期状态: $lifecycleState")
         Text("生命周期事件计数: $eventCount")
         
         Divider(modifier = Modifier.padding(vertical = 8.dp))
         
         // 示例2: 管理订阅(模拟)
         var subscriptionActive by remember { mutableStateOf(false) }
         
         DisposableEffect(Unit) {
             println("订阅开始")
             subscriptionActive = true
             
             // 模拟订阅
             val timer = Timer()
             timer.scheduleAtFixedRate(object : TimerTask() {
                 override fun run() {
                     // 这里可以执行定期任务
                 }
             }, 0, 1000)
             
             onDispose {
                 println("订阅结束")
                 subscriptionActive = false
                 timer.cancel()
             }
         }
         
         Text("订阅状态: ${if (subscriptionActive) "活跃" else "非活跃"}")
         
         Divider(modifier = Modifier.padding(vertical = 8.dp))
         
         // 示例3: 管理多个资源
         var resourceCount by remember { mutableStateOf(0) }
         
         DisposableEffect(resourceCount) {
             println("资源 $resourceCount 已创建")
             
             // 模拟多个资源
             val resources = mutableListOf<AutoCloseable>()
             resources.add(object : AutoCloseable {
                 override fun close() {
                     println("资源1已关闭")
                 }
             })
             
             resources.add(object : AutoCloseable {
                 override fun close() {
                     println("资源2已关闭")
                 }
             })
             
             onDispose {
                 println("清理资源...")
                 resources.forEach { it.close() }
             }
         }
         
         // 示例4: 键盘监听(模拟)
         var isKeyboardVisible by remember { mutableStateOf(false) }
         
         DisposableEffect(Unit) {
             println("键盘监听器已注册")
             // 这里通常会注册真实的键盘监听
             
             onDispose {
                 println("键盘监听器已移除")
             }
         }
         
         // 示例5: 结合 rememberUpdatedState 使用
         var callbackData by remember { mutableStateOf("初始数据") }
         val currentCallbackData by rememberUpdatedState(callbackData)
         
         DisposableEffect(Unit) {
             // 使用 rememberUpdatedState 确保回调中使用最新值
             val callback = object : SomeCallback {
                 override fun onEvent() {
                     println("回调中使用的数据: $currentCallbackData")
                 }
             }
             
             // 注册回调
             registerCallback(callback)
             
             onDispose {
                 unregisterCallback(callback)
             }
         }
         
         Button(onClick = { callbackData = "更新后的数据 ${Date()}" }) {
             Text("更新回调数据")
         }
     }
    

    } }

// 模拟接口 interface SomeCallback { fun onEvent() }

fun registerCallback(callback: SomeCallback) { // 模拟注册 }

fun unregisterCallback(callback: SomeCallback) { // 模拟取消注册 }

DisposableEffect 使用场景总结:

  • 适合使用DisposableEffect的场景:
    1. 生命周期监听(Activity/Fragment/Compose生命周期)
    1. 广播接收器注册和取消
    1. 事件监听器管理
    1. 数据库/网络连接管理
    1. 定时器/任务调度器
    1. 系统服务绑定
    1. 资源清理(文件句柄、流等) */

SideEffect - 同步副作用

/**

  • SideEffect - 将Compose状态同步到非Compose世界

  • 特点:

    1. 在每次成功重组后执行
    1. 没有取消机制(与LaunchedEffect不同)
    1. 执行顺序有保证(在组合完成之后)
    1. 适合与非Compose代码交互
  • @param block: 同步执行的代码块 */ @Composable fun SideEffectSection(counter: Int) { var analyticsCount by remember { mutableStateOf(0) } var logEntries by remember { mutableStateOf<List>(emptyList()) }

    Card( modifier = Modifier.fillMaxWidth(), elevation = 4.dp ) { Column( modifier = Modifier.padding(16.dp) ) { Text( text = "3. SideEffect 演示", style = MaterialTheme.typography.h6, fontWeight = FontWeight.Bold )

         // 示例1: 同步状态到分析库
         SideEffect {
             // 每次重组后都会执行
             analyticsCount++
             println("SideEffect执行次数: $analyticsCount, Counter: $counter")
             
             // 这里通常会调用非Compose代码
             // 例如:Firebase Analytics、日志记录等
             logToAnalytics("counter_updated", counter)
         }
         
         Text("SideEffect执行次数: $analyticsCount")
         
         Divider(modifier = Modifier.padding(vertical = 8.dp))
         
         // 示例2: 更新非ComposeUI
         SideEffect {
             // 更新Activity的ActionBar标题
             updateActionBarTitle("Counter: $counter")
         }
         
         // 示例3: 同步到SharedPreferences
         SideEffect {
             if (counter % 5 == 0) {
                 saveToPreferences("last_counter_value", counter)
             }
         }
         
         Divider(modifier = Modifier.padding(vertical = 8.dp))
         
         // 示例4: 日志记录
         SideEffect {
             // 记录重组信息
             val logEntry = "重组: counter=$counter, time=${Date()}"
             logEntries = logEntries + logEntry
         }
         
         // 显示日志
         if (logEntries.isNotEmpty()) {
             Text(
                 text = "最近日志:",
                 fontWeight = FontWeight.Bold
             )
             logEntries.takeLast(3).forEach { entry ->
                 Text(
                     text = entry,
                     fontSize = 12.sp,
                     maxLines = 1,
                     overflow = TextOverflow.Ellipsis
                 )
             }
         }
         
         Divider(modifier = Modifier.padding(vertical = 8.dp))
         
         // 示例5: 与WebView交互(模拟)
         var webViewState by remember { mutableStateOf("") }
         
         SideEffect {
             // 同步状态到WebView
             if (counter > 10) {
                 webViewState = "high_counter"
                 updateWebView("counter", counter)
             }
         }
         
         Text("WebView状态: $webViewState")
         
         // 示例6: 副作用顺序演示
         var effectOrder by remember { mutableStateOf("") }
         
         SideEffect {
             effectOrder += "A"
         }
         
         SideEffect {
             effectOrder += "B"
         }
         
         Text("SideEffect执行顺序: $effectOrder")
     }
    

    } }

// 模拟函数 fun logToAnalytics(event: String, value: Int) { // 模拟分析日志 }

fun updateActionBarTitle(title: String) { // 模拟更新ActionBar }

fun saveToPreferences(key: String, value: Int) { // 模拟保存到SharedPreferences }

fun updateWebView(key: String, value: Int) { // 模拟更新WebView }

SideEffect 使用场景总结:

  • 适合使用SideEffect的场景:
    1. 分析/统计事件记录
    1. 与系统UI同步(ActionBar、状态栏等)
    1. 日志记录
    1. 持久化状态(SharedPreferences)
    1. 与非ComposeUI组件交互(WebView、MapView等)
    1. 发送广播
    1. 更新非响应式系统 */