在 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 开发的首选异步方案。

相关推荐
用户69371750013847 小时前
Kotlin官方文档-基础知识-基础语法(翻译官方文档+自我总结)
kotlin
zhangphil8 小时前
Kotlin线程池newFixedThreadPoolContext与约束协程运行的线程数量limitedParallelism
kotlin
用户693717500138410 小时前
Kotlin 全量关键字全面整理,并附上简洁示例,确保每一个关键字都清楚易懂。
kotlin
消失的旧时光-19431 天前
Android ble理解
java·kotlin
studyForMokey1 天前
【Android Activity】生命周期深入理解
android·kotlin
来来走走1 天前
kotlin学习 lambda编程
android·学习·kotlin
Huang兄1 天前
kotlin协程-基础概念篇
kotlin
无知的前端1 天前
一文精通-Kotlin中双冒号:: 语法使用
android·kotlin
Huang兄1 天前
kotlin协程-基础设施篇-协程创建与启动:SafeContinuation
kotlin