三、Kotlin协程+异步加载+Loading状态

三、Kotlin协程+异步加载+Loading状态

Kotlin协程是执行异步操作的关键知识点,对于不可以在UI线程执行的耗时操作我们需要通过协程进行异步操作,加载页面。

复习状态管理,操作Loading状态

1.Kotlin协程

  • 为什么需要协程

    • Android主线程(UI)线程中不可以执行耗时操作(网络操作、数据库等)
    • 有需求需要异步执行
    • 传统方案:AsyncTask(已废弃)、Handler(复杂)
    • 使用协程可以用同步代码的风格写异步逻辑,简洁、安全、可取消
    • Kotlin的原生支持使用起来更加便捷
  • 协程的三大核心概念

    • 挂起函数(Suspend function)是一种特殊的可暂停且不阻塞线程的函数,在其中可以进行耗时操作,本质上挂起函数是一个回调函数,代码执行到挂起函数后,挂起函数在子线程中执行耗时操作,执行完毕之后回调后续代码继续运行页面逻辑。

    kotlin 复制代码
    //挂起函数模拟
    suspend fun requestNotes(){
        delay(2000)
    }

    挂起函数使用suspend修饰,其他与普通函数无异。

    • 协程作用域(CoroutineScope)是用来控制协程生命周期的一个函数/对象,常见的有LifecycleScope,viewModelScope,rememberCoroutineScope()。还有后边要讲的Jetpack Compose的副作用(LaunchedEffect)

    kotlin 复制代码
    val scope = rememberCoroutineScope()
    
    val showMsg = { msg: String ->
        scope.launch {
            snackBarHostState.showSnackbar(msg)
        }
        Unit
    }

    使用场景,例如我们显示Snackbar时,为了防止调用方处于非UI线程,所以在方法中使用协程,调用时明确在UI线程。

    • 调度器(Dispatcher)决定协程运行在哪类线程:Dispatchers.Main(UI线程),Dispatchers.IO(磁盘/网络)

    kotlin 复制代码
    //使用调度器切换到IO线程
    suspend fun requestNotes(){
        return withContext(Dispatchers.IO){
            delay(2000)
        }
        //自动切回原线程
    }
  • 协程的关键规则

    • 只能在协程或者挂起函数中调用挂起函数!!!

    • UI更新必须在Main线程

    • 不要手动创建GlobalScope(容易内存泄露)

      kotlin 复制代码
      //这是GlobalScope的用法
      GlobalScope.launch(Dispatchers.Main) {
          val data = fetchData()
          updateUI(data) // 可能在 UI 销毁后调用!
      }

      因为没有绑定自己的生命周期,GlobalScope中的代码不会随着组件销毁而销毁,所以会造成报错等难以估量的bug,所以实际中应该避免使用。

2、Jetpack Compose的副作用

  • 什么是副作用(Side Effect)?

    • 改变Compose作用域之外的状态,例如:
      • 启动协程
      • 修改ViewModel数据
      • 调用系统API(如权限要求)
    • 副作用就是UI组件Compose与外界沟通的桥梁。
    • 为什么要使用:Composable 函数可能被 频繁、多次、非预期地调用,如果不使用副作用可能会因为
  • LaunchedEffect的作用

    • key 发生变化Composable 首次进入 Composition 时,启动一个 生命周期绑定的协程

    • Composable 退出时,协程自动取消

    kotlin 复制代码
    //示例代码
    LaunchedEffect(Unit) {
        //模拟请求
        requestNotes()
        //生成模拟数据
        val mockNotes = listOf<Note>(
            Note(
                id = 1L,
                title = "协程入门",
                content = "今天学习了 Kotlin 协程和 LaunchedEffect,实现了异步加载笔记列表。",
                timestamp = "2026-01-07 09:00",
                tags = listOf("Kotlin", "Coroutines")
            ),
            Note(
                id = 2L,
                title = "状态管理进阶",
                content = "理解了 mutableStateOf 和 remember 的配合使用。",
                timestamp = "2026-01-07 10:30",
                tags = listOf("Compose", "State")
            ),
            Note(
                id = 3L,
                title = "副作用处理",
                content = "学会了用 LaunchedEffect 安全地执行异步操作。",
                timestamp = "2026-01-07 11:15",
                tags = listOf("SideEffect", "Android")
            )
        )
        notes = mockNotes
        isLoading = false
    }

    key = Unit → 只在首次进入时执行一次

    key = someId → 当 someId 变化时重新执行

  • rememberCoroutineScope的使用

    • 已经在前面介绍过了,不重复赘述

3.项目代码更新

  • 创建一个卡片列表组件

    kotlin 复制代码
    package com.jiahengfei.ktdm
    
    import androidx.compose.foundation.layout.Arrangement
    import androidx.compose.foundation.layout.Box
    import androidx.compose.foundation.layout.PaddingValues
    import androidx.compose.foundation.layout.fillMaxSize
    import androidx.compose.foundation.layout.fillMaxWidth
    import androidx.compose.foundation.lazy.LazyColumn
    import androidx.compose.foundation.lazy.items
    import androidx.compose.material3.CircularProgressIndicator
    import androidx.compose.runtime.Composable
    import androidx.compose.runtime.LaunchedEffect
    import androidx.compose.runtime.getValue
    import androidx.compose.runtime.mutableStateOf
    import androidx.compose.runtime.remember
    import androidx.compose.runtime.setValue
    import androidx.compose.ui.Alignment
    import androidx.compose.ui.Modifier
    import androidx.compose.ui.unit.dp
    import kotlinx.coroutines.Dispatchers
    import kotlinx.coroutines.delay
    import kotlinx.coroutines.withContext
    
    /**
     * Create by zFox from AndroidStudio2022.01
     * 日期:2026/1/7 19:10
     * 描述:笔记本列表
     */
    
    /**
     * 挂起函数
     */
    suspend fun requestNotes(){
        return withContext(Dispatchers.IO){
            delay(2000)
        }
    }
    
    @Composable
    fun NoteListScreen(
        modifier: Modifier = Modifier,
        showSnackBar: (String) -> Unit,
    ) {
        var notes by remember { mutableStateOf<List<Note>>(emptyList()) }
        var isLoading by remember { mutableStateOf(true) }
    
        LaunchedEffect(Unit) {
            //模拟请求
            requestNotes()
            //生成模拟数据
            val mockNotes = listOf<Note>(
                Note(
                    id = 1L,
                    title = "协程入门",
                    content = "今天学习了 Kotlin 协程和 LaunchedEffect,实现了异步加载笔记列表。",
                    timestamp = "2026-01-07 09:00",
                    tags = listOf("Kotlin", "Coroutines")
                ),
                Note(
                    id = 2L,
                    title = "状态管理进阶",
                    content = "理解了 mutableStateOf 和 remember 的配合使用。",
                    timestamp = "2026-01-07 10:30",
                    tags = listOf("Compose", "State")
                ),
                Note(
                    id = 3L,
                    title = "副作用处理",
                    content = "学会了用 LaunchedEffect 安全地执行异步操作。",
                    timestamp = "2026-01-07 11:15",
                    tags = listOf("SideEffect", "Android")
                )
            )
            notes = mockNotes
            isLoading = false
        }
    
        Box(
            modifier = modifier.fillMaxSize(),
            contentAlignment = Alignment.Center
        ) {
            if (isLoading) {
                CircularProgressIndicator()
            } else {
                //显示笔记本列表
                LazyColumn(
                    modifier = Modifier.fillMaxSize(),
                    verticalArrangement = Arrangement.spacedBy(8.dp),
                    contentPadding = PaddingValues(16.dp)
                ) {
                    items(notes) { note ->
                        NoteCard(
                            note = note,
                            showSnackBar,
                            modifier = Modifier.fillParentMaxWidth()
                        )
                    }
                }
            }
        }
    }

    这是新的入口 Composable,替代之前的单卡片测试

  • 在MainActivity中使用这个组件

    kotlin 复制代码
    package com.jiahengfei.ktdm
    
    import android.os.Bundle
    import android.widget.TextView
    import android.widget.Toast
    import androidx.activity.ComponentActivity
    import androidx.activity.compose.setContent
    import androidx.compose.foundation.layout.Column
    import androidx.compose.foundation.layout.fillMaxWidth
    import androidx.compose.foundation.layout.systemBarsPadding
    import androidx.compose.material3.MaterialTheme
    import androidx.compose.material3.Scaffold
    import androidx.compose.material3.SnackbarHost
    import androidx.compose.material3.SnackbarHostState
    import androidx.compose.material3.Surface
    import androidx.compose.runtime.remember
    import androidx.compose.runtime.rememberCoroutineScope
    import androidx.compose.ui.Modifier
    import com.jiahengfei.jnote.ui.theme.NoteAppTheme
    import kotlinx.coroutines.launch
    
    /**
     * ================================================
     * 项    目:ktdm
     * 日    期:2026/1/5 19:17
     * 包    名:com.jiahengfei.ktdm
     * 描    述:进阶的第一行代码
     * Create by zFox from AndroidStudio2020.3
     * ================================================
     */
    
    class MainActivity : ComponentActivity() {
    
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
    
            setContent {
                NoteAppTheme() {
                    val snackBarHostState = remember { SnackbarHostState() }
                    val scope = rememberCoroutineScope()
    
                    Scaffold(
                        snackbarHost = { SnackbarHost(snackBarHostState) }
                    ) {
                        Surface(
                            modifier = Modifier
                                .fillMaxWidth()
                                .systemBarsPadding(),
                            color = MaterialTheme.colorScheme.background,
                        ) {
    
                            val showMsg = { msg: String ->
                                scope.launch {
                                    snackBarHostState.showSnackbar(msg)
                                }
                                Unit
                            }
    
                            NoteListScreen(
                                showSnackBar = showMsg
                            )
    
    //                        Column(
    //                            modifier = Modifier
    //                                .fillMaxWidth()
    //                        ) {
    //                            val sampleNote = Note(
    //                                1L,
    //                                "第一天学习笔记",
    //                                "今天非常之Nice!",
    //                                "2026-01-05",
    //                            )
    //                            NoteCard(sampleNote, showMsg)
    //
    //                            val sampleNote2 = Note(
    //                                2L,
    //                                "第二天学习笔记",
    //                                "今天学习了 Kotlin 扩展函数和 Compose 状态管理,实现了可交互的笔记卡片!",
    //                                "2026-01-06",
    //                                listOf("Kotlin", "Compose", "Android")
    //                            )
    //                            NoteCard(sampleNote2, showMsg)
    //                        }
                        }
                    }
                }
            }
        }
    
    }
  • 运行效果

这就是第三天的全部知识点了,明天我们将学习使用ViewModel统一管理状态!

相关推荐
段娇娇29 分钟前
Android jetpack LiveData (三) 粘性数据(数据倒灌)问题分析及解决方案
android·android jetpack
jzlhll1232 小时前
Kotlin Mutex vs Java ReentrantLock vs synchronized
java·开发语言·kotlin
Kapaseker2 小时前
一杯 Kotlin 美式品味 object 声明
android·kotlin
俩个逗号。。3 小时前
Kotlin 扩展函数详解
开发语言·kotlin
段娇娇11 小时前
Android jetpack LiveData(一)使用篇
android·android jetpack
XiaoLeisj12 小时前
Android Jetpack 页面架构实战:从 LiveData、ViewModel 到 DataBinding 的生命周期管理与数据绑定
android·java·架构·android jetpack·livedata·viewmodel·databinding
阿巴斯甜17 小时前
Compose中 组件的状态总结:
android jetpack
阿巴斯甜18 小时前
Compose中Icon的使用:
android jetpack
阿巴斯甜18 小时前
Compose中Image的使用
android jetpack
阿巴斯甜19 小时前
Compose中 buildAnnotatedString的使用:
android jetpack