三、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统一管理状态!

相关推荐
shuangrenlong18 小时前
笔记kotlin注意的点
kotlin
Android-Flutter21 小时前
android compose LazyVerticalGrid上下滚动的网格布局 使用
android·kotlin
Android-Flutter21 小时前
android compose LazyHorizontalGrid水平滚动的网格布局 使用
android·kotlin
我命由我1234521 小时前
Kotlin 面向对象 - 装箱与拆箱
android·java·开发语言·kotlin·android studio·android jetpack·android-studio
我命由我1234521 小时前
Android Jetpack Compose - Snackbar、Box
android·java·java-ee·kotlin·android studio·android jetpack·android-studio
alexhilton1 天前
Jetpack Compose内部的不同节点类型
android·kotlin·android jetpack
我命由我123452 天前
Kotlin 运算符 - == 运算符与 === 运算符
android·java·开发语言·java-ee·kotlin·android studio·android-studio
我命由我123452 天前
Android Jetpack Compose - TopAppBar、BottomAppBar、Scaffold
android·java·java-ee·kotlin·android studio·android jetpack·android-studio
a3158238062 天前
Android 大图显示策略优化显示(二)
android·java·开发语言·javascript·kotlin·glide·图片加载