知用网
柔彩主题三 · 更轻盈的阅读体验

Kotlin协程在Android中的实际使用技巧

发布时间:2025-12-13 05:50:55 阅读:564 次

为什么用协程处理Android异步任务

在开发Android应用时,经常要从网络拉数据、读写数据库,或者做些耗时的计算。以前常用Handler、AsyncTask甚至开Thread,代码嵌套多,回调层层叠,维护起来头疼。自从Kotlin协程推出后,异步操作变得像写同步代码一样清晰。

比如你做个天气App,点“刷新”要从服务器取最新数据,同时更新UI。用传统方式得切线程来回跳,协程几行代码就搞定。

基础概念:CoroutineScope、launch和async

协程运行需要一个作用域(CoroutineScope),Android中通常用lifecycleScope或viewModelScope,它们会自动管理生命周期,避免内存泄漏。

lifecycleScope.launch {
    val weatherData = async(Dispatchers.IO) {
        repository.fetchWeather()
    }
    updateUi(weatherData.await())
}

上面这段代码在按钮点击后启动协程,async开启一个IO线程去请求数据,主线程等待结果后更新界面。整个过程线性表达,逻辑一目了然。

切换线程就这么简单

协程通过Dispatchers控制运行线程。常见有三个:

  • Dispatchers.Main:用于更新UI
  • Dispatchers.IO:适合读写文件、网络请求
  • Dispatchers.Default:适合CPU密集型计算

比如你想先查本地缓存,没有再走网络,可以这样写:

lifecycleScope.launch {
    val data = withContext(Dispatchers.IO) {
        val cached = db.weatherDao().getLast()
        if (cached != null && !isExpired(cached)) {
            cached
        } else {
            val remote = api.getLatestWeather()
            db.weatherDao().insert(remote)
            remote
        }
    }
    binding.tempText.text = "${data.temp}°C"
}

withContext直接切换到IO线程执行数据库和网络操作,结束后自动回到原线程更新UI,不用手动post。

多个任务并行怎么做

假如你的首页要同时加载用户信息、通知数量和配置项,三个接口互不依赖,就可以并行发起。

lifecycleScope.launch {
    val userDeferred = async { userRepository.getUser() }
    val notifyDeferred = async { notifyRepository.getCount() }
    val configDeferred = async { configRepository.load() }

    val user = userDeferred.await()
    val notifyCount = notifyDeferred.await()
    val config = configDeferred.await()

    renderHome(user, notifyCount, config)
}

三个async同时开始,各自跑在自己的线程里,await时才阻塞等待结果。整体耗时等于最慢的那个请求,效率提升明显。

异常处理别忘了

协程里抛异常不会自动冒泡到主线程,得自己捕获。推荐用try-catch包住launch里的逻辑。

lifecycleScope.launch {
    try {
        val result = api.getData()
        showSuccess(result)
    } catch (e: Exception) {
        showError("加载失败,请重试")
    }
}

如果多个子协程都要统一处理错误,可以用SupervisorJob配合CoroutineExceptionHandler。

结合ViewModel更顺手

在MVVM架构中,ViewModel里用viewModelScope发请求最合适。页面销毁时,协程自动取消,不会造成崩溃。

class MainViewModel : ViewModel() {
    fun loadUserData() {
        viewModelScope.launch {
            try {
                val user = userRepository.fetch()
                _uiState.value = UserLoaded(user)
            } catch (e: IOException) {
                _uiState.value = LoadFailed
            }
        }
    }
}

Activity或Fragment观察_uiState即可更新界面,完全不用操心协程生命周期。

实际场景:下拉刷新+加载更多

列表页很常见既要支持刷新,又要分页加载。两个操作都用协程处理,互不干扰。

// 下拉刷新
binding.swipeRefresh.setOnRefreshListener {
    lifecycleScope.launch {
        refreshData()
        binding.swipeRefresh.isRefreshing = false
    }
}

// 滑动加载
adapter.onLoadMore {
    lifecycleScope.launch {
        val newItems = fetchNextPage()
        adapter.addItems(newItems)
    }
}

每个操作独立启协程,即使用户快速连点刷新,也不会堆叠多个网络请求。