在 Android 中使用协程(Coroutine)

在 Android 中使用协程(Coroutine)是简化异步操作(如网络请求、数据库操作、UI 更新)的最佳实践之一。它能以同步代码的形式编写异步逻辑,避免回调地狱,同时自动处理线程切换,非常适合 Android 开发场景。

一、准备工作:添加依赖

首先,在 app/build.gradle 中添加协程相关依赖(确保版本兼容):

gradle

复制代码
dependencies {
    // 核心协程库
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
    // Android 专用协程(含主线程调度器等)
    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4'
    // (可选)与生命周期组件集成(如 lifecycleScope)
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.6.2'
    // (可选)与 ViewModel 集成(viewModelScope)
    implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2'
}

二、核心概念与 Android 场景结合

在 Android 中使用协程,需重点关注 作用域(Scope)调度器(Dispatcher)暂停函数(Suspend Function),它们直接影响协程的生命周期和线程管理。

1. 协程作用域(Coroutine Scope)

作用域用于管理协程的生命周期,确保协程在不需要时被取消(如页面销毁时),避免内存泄漏。Android 中常用的作用域:

作用域 适用场景 生命周期
lifecycleScope Activity/Fragment 随组件销毁(onDestroy)而取消
viewModelScope ViewModel 随 ViewModel 销毁而取消
CoroutineScope 自定义场景(如全局任务) 需手动管理(通过 Job 取消)

示例:在 Activity 中使用 lifecycleScope lifecycleScopeandroidx.lifecycle:lifecycle-runtime-ktx 提供的扩展,自动绑定组件生命周期:

kotlin

复制代码
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 启动协程(自动在 Activity 销毁时取消)
        lifecycleScope.launch {
            // 协程逻辑(如网络请求)
            val data = fetchDataFromNetwork()
            // 自动切换到主线程更新 UI
            binding.textView.text = data
        }
    }
}

示例:在 ViewModel 中使用 viewModelScope viewModelScope 随 ViewModel 生命周期自动取消,适合数据加载:

kotlin

复制代码
class MyViewModel : ViewModel() {
    fun loadData() {
        // 启动协程(ViewModel 销毁时自动取消)
        viewModelScope.launch {
            val data = repository.getData()
            // 更新 LiveData(通知 UI)
            _uiState.value = data
        }
    }
}
2. 调度器(Dispatcher):指定协程运行的线程

Android 中关键的调度器:

调度器 作用 适用场景
Dispatchers.Main 运行在 Android 主线程(UI 线程) 更新 UI(如设置 TextView 文本)
Dispatchers.IO 运行在 IO 线程池(专门处理 IO 操作) 网络请求、数据库操作、文件读写
Dispatchers.Default 运行在 CPU 密集型线程池 复杂计算、数据排序等 CPU 密集任务

注意

  • lifecycleScopeviewModelScope 的默认调度器是 Dispatchers.Main(主线程),但可通过参数指定其他调度器。
  • 耗时操作(如网络请求)必须放在 Dispatchers.IODispatchers.Default 中,避免阻塞主线程。
3. 暂停函数(Suspend Function)

suspend 关键字标记的函数,只能在协程或其他暂停函数中调用。它可以「暂停」执行(释放线程),待任务完成后再「恢复」,但不会阻塞线程。

示例:定义一个网络请求的暂停函数 结合 Retrofit 使用时,可直接将接口方法定义为 suspend 函数(Retrofit 2.6+ 支持):

kotlin

复制代码
// 网络接口(Retrofit)
interface ApiService {
    @GET("data")
    suspend fun fetchData(): Response<Data> // 暂停函数,自动在 IO 线程执行
}

// 仓库层调用
class DataRepository(private val api: ApiService) {
    // 暂停函数:封装网络请求
    suspend fun getData(): Data {
        val response = api.fetchData()
        if (response.isSuccessful) {
            return response.body() ?: throw Exception("空数据")
        } else {
            throw Exception("请求失败")
        }
    }
}

三、完整示例:从网络请求到更新 UI

下面是一个完整流程:在 Activity 中启动协程 → 切换到 IO 线程执行网络请求 → 切换回主线程更新 UI。

kotlin

复制代码
class MainActivity : AppCompatActivity() {
    private val apiService by lazy { RetrofitClient.create(ApiService::class.java) }
    private val repository by lazy { DataRepository(apiService) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // 点击按钮触发数据加载
        binding.loadButton.setOnClickListener {
            loadData()
        }
    }

    private fun loadData() {
        // 用 lifecycleScope 启动协程(默认在主线程)
        lifecycleScope.launch {
            // 显示加载中状态
            binding.progressBar.visibility = View.VISIBLE

            try {
                // 切换到 IO 线程执行网络请求(withContext 会暂停当前协程,执行完后恢复)
                val data = withContext(Dispatchers.IO) {
                    repository.getData() // 调用暂停函数
                }

                // 自动切回主线程,更新 UI
                binding.textView.text = data.content
                binding.progressBar.visibility = View.GONE
            } catch (e: Exception) {
                // 处理异常(如网络错误)
                binding.textView.text = "加载失败:${e.message}"
                binding.progressBar.visibility = View.GONE
            }
        }
    }
}

关键说明

  • withContext(Dispatchers.IO):将代码块切换到 IO 线程执行,执行完毕后自动切回原调度器(这里是主线程)。
  • try-catch:捕获网络请求可能抛出的异常(如超时、数据为空),避免应用崩溃。

四、取消协程(避免内存泄漏)

协程作用域(如 lifecycleScopeviewModelScope)会自动取消协程,但如果是自定义作用域,需手动管理:

kotlin

复制代码
// 自定义作用域(需手动取消)
val customScope = CoroutineScope(Dispatchers.Main + Job())

fun startTask() {
    customScope.launch {
        repeat(10) {
            delay(1000) // 模拟耗时操作
            Log.d("Coroutine", "执行中:$it")
        }
    }
}

// 在不需要时取消(如页面销毁)
fun cancelTask() {
    customScope.cancel() // 取消作用域内所有协程
}

五、常见场景与最佳实践

  1. 并行请求 :用 async 同时发起多个请求,再用 awaitAll 等待全部完成:

    kotlin

    复制代码
    lifecycleScope.launch {
        val deferred1 = async(Dispatchers.IO) { repository.getData1() }
        val deferred2 = async(Dispatchers.IO) { repository.getData2() }
        val result1 = deferred1.await()
        val result2 = deferred2.await()
        // 处理两个结果
    }
  2. 延迟任务 :用 delay() 替代 Handler.postDelayed()

    kotlin

    复制代码
    lifecycleScope.launch {
        delay(2000) // 延迟 2 秒(非阻塞)
        binding.textView.text = "延迟后更新"
    }
  3. 与 Room 配合 :Room 支持 suspend 函数,直接在协程中调用数据库操作:

    kotlin

    复制代码
    @Dao
    interface UserDao {
        @Query("SELECT * FROM user")
        suspend fun getUsers(): List<User> // 暂停函数,自动在 IO 线程执行
    }

总结

在 Android 中使用协程的核心是:

  • lifecycleScopeviewModelScope 管理生命周期,避免泄漏;
  • Dispatchers.IO 处理耗时操作,Dispatchers.Main 更新 UI;
  • suspend 函数封装异步逻辑,用 withContext 切换线程。

协程让异步代码更简洁、可读性更强,是 Android 开发的首选异步方案。

相关推荐
Kapaseker7 小时前
一万四千字重温 Android 四大组件
android·kotlin
我爱烤冷面8 小时前
kotlin项目实现Java doc的方案:使用Dokka
java·开发语言·kotlin·dokka
jian110588 小时前
android java转kotlin,kotlin转java
android·java·kotlin
ForteScarlet8 小时前
Kotlin 2.3.0 现已发布!又有什么好东西?
android·开发语言·后端·ios·kotlin
ULTRA??2 天前
归并排序算法实现,kotlin,c++,python
c++·python·kotlin
Kapaseker2 天前
四大组件齐上阵,轻松拿捏实习生
android·kotlin
Kapaseker3 天前
三分钟搞懂 Kotlin Flow 中的背压
android·kotlin
QING6183 天前
Jetpack Compose 中的 ViewModel 作用域管理 —— 新手指南
android·kotlin·android jetpack
KotlinKUG贵州4 天前
Kotlin/Ktor 实践:利用 MCP 从零打造 AI Agent 服务端指南
kotlin·agent·mcp
喜熊的Btm4 天前
探索 Kotlin 的不可变集合库
kotlin·android jetpack