LaunchedEffect - 协程副作用
/**
-
LaunchedEffect - 用于在Composable中启动协程
-
- 当LaunchedEffect进入组合时启动协程
-
- 当LaunchedEffect退出组合或key变化时取消协程
-
- 协程在Compose的协程作用域中运行
-
- 适合执行挂起函数:动画、网络请求等
-
@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的场景:
-
- 一次性初始化操作
-
- 响应状态变化的副作用
-
- 动画控制
-
- 网络请求
-
- 定时任务
-
-
事件监听和响应 */
-
DisposableEffect - 资源管理副作用
/**
-
DisposableEffect - 用于管理需要清理的资源
-
特点:
-
- 当DisposableEffect进入组合时执行onStart
-
- 当DisposableEffect退出组合或key变化时执行onDispose
-
- 必须包含onDispose子句
-
- 适合管理:事件监听、广播接收器、资源连接等
-
@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的场景:
-
- 生命周期监听(Activity/Fragment/Compose生命周期)
-
- 广播接收器注册和取消
-
- 事件监听器管理
-
- 数据库/网络连接管理
-
- 定时器/任务调度器
-
- 系统服务绑定
-
-
资源清理(文件句柄、流等) */
-
SideEffect - 同步副作用
/**
-
SideEffect - 将Compose状态同步到非Compose世界
-
特点:
-
- 在每次成功重组后执行
-
- 没有取消机制(与LaunchedEffect不同)
-
- 执行顺序有保证(在组合完成之后)
-
- 适合与非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的场景:
-
- 分析/统计事件记录
-
- 与系统UI同步(ActionBar、状态栏等)
-
- 日志记录
-
- 持久化状态(SharedPreferences)
-
- 与非ComposeUI组件交互(WebView、MapView等)
-
- 发送广播
-
-
更新非响应式系统 */
-