Jorgen's blog Jorgen's blog
首页
  • 平台架构
  • 混合式开发记录
  • 推送服务
  • 数据分析
  • 实时调度
  • 架构思想

    • 分布式
  • 编程框架工具

    • 编程语言
    • 框架
    • 开发工具
  • 数据存储与处理

    • 数据库
    • 大数据
  • 消息、缓存与搜索

    • 消息队列
    • 搜索与日志分析
  • 前端与跨端开发

    • 前端技术
    • Android
  • 系统与运维

    • 操作系统
    • 容器化与 DevOps
  • 物联网与安全

    • 通信协议
    • 安全
    • 云平台
newland
  • 关于我
  • 终身学习
  • 关于时间的感悟
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

jorgen

Love it, make mistakes, learn, keep grinding.
首页
  • 平台架构
  • 混合式开发记录
  • 推送服务
  • 数据分析
  • 实时调度
  • 架构思想

    • 分布式
  • 编程框架工具

    • 编程语言
    • 框架
    • 开发工具
  • 数据存储与处理

    • 数据库
    • 大数据
  • 消息、缓存与搜索

    • 消息队列
    • 搜索与日志分析
  • 前端与跨端开发

    • 前端技术
    • Android
  • 系统与运维

    • 操作系统
    • 容器化与 DevOps
  • 物联网与安全

    • 通信协议
    • 安全
    • 云平台
newland
  • 关于我
  • 终身学习
  • 关于时间的感悟
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Capacitor浅析
  • ADB调试
  • mlkit
  • face detection
  • Android中的Kotlin协程:告别回调地狱
    • 前言
    • 什么是协程?
    • 为什么在Android中使用协程?
    • 基本使用
      • 添加依赖
      • 启动一个简单的协程
      • 在Android中使用协程
    • 协程构建器
      • launch
      • async
      • runBlocking
    • 协程作用域
      • viewModelScope
      • lifecycleScope
    • 调度器
    • 错误处理
    • 协程上下文
    • 协程取消
    • 实战示例:网络请求与UI更新
    • 流(Flow)
    • 最佳实践
    • 结语
  • Android性能优化实践与技巧
  • Android Jetpack Compose入门:构建现代UI的全新方式
  • Android Jetpack Compose入门与实践 - 构建现代Android UI
  • Android Jetpack Compose入门指南 - 构建现代UI的全新方式
  • Android Jetpack与架构组件-构建现代化应用
  • Android Jetpack架构组件:ViewModel与LiveData实战指南
  • Android Jetpack组件:构建现代Android应用的核心利器
  • Android Jetpack组件详解:构建现代化应用的核心
  • Android数据绑定与MVVM架构-构建现代化应用
  • Android架构组件:构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基石
  • Android架构组件与Jetpack-构建现代化应用的基础
  • Android架构设计之MVVM模式实战指南
  • 拥抱未来:Android Jetpack Compose入门指南
  • Android权限管理完全指南-从基础到实践
  • Android测试指南-从单元测试到UI测试的实践之路
  • Android依赖注入实战:从Dagger到Hilt的进化之路
  • Android网络编程完全指南-从HTTP到Retrofit的实践之路
  • Android数据持久化完全指南-从SharedPreferences到Room数据库
  • Android多线程与并发处理完全指南-从Thread到协程的进阶之路
  • Android应用打包与发布全流程指南-从签名到上架的实战之路
  • Android安全完全指南-从基础防护到高级加密的实战之路
  • android
Jorgen
2023-11-15
目录

Android中的Kotlin协程:告别回调地狱

# 前言

作为一名Android开发者,你是否曾因为复杂的异步代码而感到头疼?😵‍💫 回调嵌套、线程切换、异常处理...这些繁琐的工作让我们在编写Android应用时常常感到力不从心。

幸运的是,Kotlin协程为我们提供了一种优雅的解决方案。🎉 在这篇文章中,我将分享我在Android开发中使用协程的经验,带你告别回调地狱,拥抱更简洁、更强大的异步编程方式。

# 什么是协程?

协程是一种并发编程的方式,它允许我们在代码中编写看起来像同步的代码,但实际上是异步执行的。🤔 简单来说,协程就像是一种轻量级的线程,它可以在不阻塞线程的情况下挂起和恢复执行。

提示

协程并不是什么新技术,它在很多编程语言中早已存在。Kotlin对协程提供了强大的语言级支持,使得使用协程变得异常简单。

# 为什么在Android中使用协程?

在Android开发中,协程可以解决许多常见问题:

  1. 简化异步代码 - 将嵌套的回调转换为线性的代码结构
  2. 避免内存泄漏 - 通过结构化并发确保任务在适当的时候取消
  3. 简化线程切换 - 轻松在主线程和后台线程之间切换
  4. 更好的异常处理 - 使用try-catch块处理异步操作中的异常

# 基本使用

# 添加依赖

首先,确保你的项目中添加了协程依赖:

// build.gradle (Module: app)
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
1
2

# 启动一个简单的协程

import kotlinx.coroutines.*

fun main() {
    // 创建一个新的协程作用域
    CoroutineScope(Dispatchers.Default).launch {
        delay(1000) // 挂起当前协程1秒
        println("Hello from coroutine!")
    }
    
    println("Hello from main thread!")
    Thread.sleep(2000) // 阻塞主线程2秒,确保协程执行完毕
}
1
2
3
4
5
6
7
8
9
10
11
12

# 在Android中使用协程

在Android中,我们通常使用viewModelScope或lifecycleScope来管理协程的生命周期:

// 在Activity或Fragment中
lifecycleScope.launch {
    // 在主线程执行
    withContext(Dispatchers.Main) {
        textView.text = "Loading..."
    }
    
    // 切换到IO线程执行网络请求
    val result = withContext(Dispatchers.IO) {
        api.fetchData()
    }
    
    // 切换回主线程更新UI
    withContext(Dispatchers.Main) {
        textView.text = result
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 协程构建器

Kotlin提供了几种协程构建器,让我们能够以不同的方式启动协程:

# launch

launch构建器用于启动一个不返回结果的协程:

launch {
    delay(1000)
    println("This is a launch coroutine")
}
1
2
3
4

# async

async构建器用于启动一个返回结果的协程:

val deferred = async {
    delay(1000)
    "Result from async"
}

// 稍后获取结果
val result = deferred.await()
1
2
3
4
5
6
7

# runBlocking

runBlocking用于阻塞当前线程直到协程完成(通常不推荐在Android中使用):

runBlocking {
    delay(1000)
    println("This will block the current thread")
}
1
2
3
4

# 协程作用域

在Android中,正确管理协程的生命周期非常重要,避免内存泄漏和资源浪费。

# viewModelScope

与ViewModel绑定的作用域,当ViewModel被清除时,所有协程都会自动取消:

viewModelScope.launch {
    // 当ViewModel被销毁时,这个协程会自动取消
    val data = repository.loadData()
    _uiState.value = UIState.Success(data)
}
1
2
3
4
5

# lifecycleScope

与Lifecycle绑定的作用域,当Lifecycle处于DESTROYED状态时,所有协程都会自动取消:

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        // 当Lifecycle至少处于STARTED状态时才会执行
        flow.collect { value ->
            updateUI(value)
        }
    }
}
1
2
3
4
5
6
7
8

# 调度器

调度器决定了协程在哪个线程上执行:

  • Dispatchers.Main - Android主线程,用于UI更新
  • Dispatchers.IO - 适合网络请求和数据库操作
  • Dispatchers.Default - 适合CPU密集型任务
  • Dispatchers.Unconfined - 不受限制的调度器
viewModelScope.launch(Dispatchers.IO) {
    // 在IO线程执行网络请求
    val data = api.fetchData()
    
    withContext(Dispatchers.Main) {
        // 切换到主线程更新UI
        textView.text = data
    }
}
1
2
3
4
5
6
7
8
9

# 错误处理

协程中的异常处理与常规Kotlin代码类似:

viewModelScope.launch {
    try {
        val data = api.fetchData()
        _uiState.value = UIState.Success(data)
    } catch (e: Exception) {
        _uiState.value = UIState.Error(e.message ?: "Unknown error")
    }
}
1
2
3
4
5
6
7
8

# 协程上下文

协程上下文包含了协程的执行环境和配置:

launch(Dispatchers.IO + CoroutineName("MyCoroutine")) {
    // 在IO线程上执行,协程名称为"MyCoroutine"
    println(coroutineContext[CoroutineName])
}
1
2
3
4

# 协程取消

协程可以被取消,取消时会抛出CancellationException:

val job = launch {
    repeat(1000) { i ->
        delay(100)
        println("I'm sleeping $i...")
    }
}
    
delay(500) // 等待一段时间
job.cancel() // 取消协程
job.join() // 等待协程完全取消
println("Main is running")
1
2
3
4
5
6
7
8
9
10
11

# 实战示例:网络请求与UI更新

下面是一个完整的示例,展示如何使用协程进行网络请求并更新UI:

class MyViewModel(private val repository: DataRepository) : ViewModel() {
    
    private val _uiState = MutableStateFlow<UIState>(UIState.Idle)
    val uiState: StateFlow<UIState> = _uiState.asStateFlow()
    
    fun loadData() {
        viewModelScope.launch {
            _uiState.value = UIState.Loading
            
            try {
                val data = withContext(Dispatchers.IO) {
                    repository.fetchData()
                }
                _uiState.value = UIState.Success(data)
            } catch (e: Exception) {
                _uiState.value = UIState.Error(e.message ?: "Unknown error")
            }
        }
    }
}

sealed class UIState {
    object Idle : UIState()
    object Loading : UIState()
    data class Success(val data: String) : UIState()
    data class Error(val message: String) : UIState()
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

在Activity中观察UI状态:

lifecycleScope.launch {
    viewModel.uiState.collect { state ->
        when (state) {
            is UIState.Loading -> {
                progressBar.visibility = View.VISIBLE
                textView.visibility = View.GONE
            }
            is UIState.Success -> {
                progressBar.visibility = View.GONE
                textView.visibility = View.VISIBLE
                textView.text = state.data
            }
            is UIState.Error -> {
                progressBar.visibility = View.GONE
                textView.visibility = View.VISIBLE
                textView.text = "Error: ${state.message}"
            }
            is UIState.Idle -> {
                // 初始状态
            }
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

# 流(Flow)

Flow是协程中的一个重要概念,它允许我们处理异步数据流:

fun fetchData(): Flow<String> = flow {
    // 在IO线程执行
    withContext(Dispatchers.IO) {
        repeat(5) { i ->
            delay(1000)
            emit("Data $i") // 发送数据
        }
    }
}

// 在ViewModel中使用
viewModelScope.launch {
    repository.fetchData()
        .flowOn(Dispatchers.IO) // 指定上游操作在IO线程执行
        .collect { data ->
            _uiState.value = UIState.Success(data)
        }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

# 最佳实践

  1. 始终使用适当的作用域 - 在Android中使用viewModelScope或lifecycleScope
  2. 避免在主线程执行耗时操作 - 使用withContext(Dispatchers.IO)切换线程
  3. 正确处理异常 - 使用try-catch块捕获协程中的异常
  4. 避免在协程中阻塞线程 - 使用delay()而不是Thread.sleep()
  5. 使用结构化并发 - 确保协程在适当的时候被取消

# 结语

Kotlin协程为Android开发者提供了一种强大而优雅的异步编程方式。通过使用协程,我们可以编写出更简洁、更易读、更易维护的异步代码。

协程虽然概念简单,但要真正掌握它还需要在实践中不断探索。希望这篇文章能够帮助你入门Kotlin协程,并在你的Android开发中发挥它的威力。

"协程不是魔法,它只是让异步编程变得简单而强大。" —— Kotlin协程爱好者

如果你有任何问题或建议,欢迎在评论区留言交流!👇

#kotlin#协程#异步编程#android开发
上次更新: 2026/01/28, 14:21:05
face detection
Android性能优化实践与技巧

← face detection Android性能优化实践与技巧→

最近更新
01
LLM
01-30
02
intro
01-30
03
intro
01-30
更多文章>
Theme by Vdoing | Copyright © 2019-2026 Jorgen | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式