欢迎光临
我们一直在努力

【Kotlin系列17】协程在Android中的最佳实践:从入门到精通

引言

“用户点击按钮后应用卡死2秒,然后突然显示数据。”——这是我在Code Review时听到最多的问题描述之一。

几年前,我接手过一个Android项目,代码里充斥着各种Thread { … }.start(),还有大量的runOnUiThread,以及让人头疼的内存泄漏。每次用户旋转屏幕,网络请求都会重新发起;当用户快速点击按钮时,多个请求并发执行导致界面显示错乱。最糟糕的是,当Activity销毁后,后台线程仍在运行,持有Activity引用,造成内存泄漏。

Kotlin协程彻底改变了这一切。 配合Android的生命周期感知组件(ViewModel、Lifecycle),协程让异步编程变得简单、安全、高效。Google官方已经将协程作为Android异步编程的首选方案,Jetpack库全面支持协程。

本文将系统讲解Kotlin协程在Android开发中的最佳实践,涵盖:

  • 协程作用域的正确使用(lifecycleScope、viewModelScope、GlobalScope)
  • 网络请求、数据库操作、文件读写的协程实践
  • UI更新与线程切换
  • 异常处理与错误传播
  • 性能优化与常见陷阱

无论你是协程新手,还是想深入掌握协程在Android中的应用,这篇文章都会给你带来价值。


一、Android协程生态概览

1.1 协程在Android架构中的位置

在这里插入图片描述

在现代Android开发中,协程与以下组件深度集成:

架构组件层:

  • ViewModel – 提供viewModelScope,自动取消协程
  • Lifecycle – 提供lifecycleScope,生命周期感知
  • LiveData – 通过liveData {}构建器支持协程
  • Room – 原生支持suspend函数

网络层:

  • Retrofit – 通过suspend关键字支持协程
  • OkHttp – 非阻塞I/O,与协程完美配合

数据持久化层:

  • Room Database – @Dao接口支持suspend函数
  • DataStore – 完全基于协程和Flow

1.2 核心依赖配置

在build.gradle.kts中添加必要的依赖:

dependencies {
// 协程核心库
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")

// Lifecycle与协程集成
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.7.0")
implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.7.0")

// Room与协程集成
implementation("androidx.room:room-ktx:2.6.1")

// Retrofit与协程集成
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.jakewharton.retrofit:retrofit2-kotlin-coroutines-adapter:0.9.2")
}

1.3 三大协程作用域

Android提供了三个主要的协程作用域:

作用域生命周期调度器使用场景
viewModelScope ViewModel生命周期 Dispatchers.Main ViewModel中的数据加载
lifecycleScope Activity/Fragment生命周期 Dispatchers.Main UI相关操作
GlobalScope 应用进程生命周期 需手动指定 ❌ 通常不推荐使用

核心原则: 始终使用生命周期感知的作用域,避免内存泄漏和不必要的计算。


二、ViewModel中的协程实践

2.1 viewModelScope的正确使用

viewModelScope是ViewModel的扩展属性,当ViewModel被清除时自动取消所有协程。

基本用法:

class UserViewModel(
private val userRepository: UserRepository
) : ViewModel() {

private val _userState = MutableStateFlow<UiState<User>>(UiState.Loading)
val userState: StateFlow<UiState<User>> = _userState.asStateFlow()

fun loadUser(userId: String) {
viewModelScope.launch {
_userState.value = UiState.Loading

try {
val user = userRepository.getUser(userId)
_userState.value = UiState.Success(user)
} catch (e: Exception) {
_userState.value = UiState.Error(e.message ?: "Unknown error")
}
}
}
}

sealed class UiState<out T> {
object Loading : UiState<Nothing>()
data class Success<T>(val data: T) : UiState<T>()
data class Error(val message: String) : UiState<Nothing>()
}

关键点:

  • 使用viewModelScope.launch启动协程,无需手动取消
  • 使用StateFlow暴露UI状态,替代LiveData
  • 在协程中处理异常,转换为UI状态

2.2 并发请求优化

场景: 需要同时加载用户信息、好友列表和帖子列表。

❌ 错误方式:串行执行

// 串行执行:总耗时 = time1 + time2 + time3
suspend fun loadAllData(userId: String) {
val user = userRepository.getUser(userId) // 1秒
val friends = friendsRepository.getFriends(userId) // 1秒
val posts = postsRepository.getPosts(userId) // 1秒
// 总耗时:3秒
}

✅ 正确方式:并发执行

import kotlinx.coroutines.async
import kotlinx.coroutines.awaitAll

suspend fun loadAllData(userId: String): Triple<User, List<Friend>, List<Post>> {
return coroutineScope {
// 并发执行三个请求
val userDeferred = async { userRepository.getUser(userId) }
val friendsDeferred = async { friendsRepository.getFriends(userId) }
val postsDeferred = async { postsRepository.getPosts(userId) }

// 等待所有结果
Triple(
userDeferred.await(),
friendsDeferred.await(),
postsDeferred.await()
)
}
// 总耗时:约1秒(最慢的那个请求)
}

在ViewModel中使用:

class DashboardViewModel(
private val userRepository: UserRepository,
private val friendsRepository: FriendsRepository,
private val postsRepository: PostsRepository
) : ViewModel() {

private val _dashboardState = MutableStateFlow<DashboardState>(DashboardState.Loading)
val dashboardState = _dashboardState.asStateFlow()

fun loadDashboard(userId: String) {
viewModelScope.launch {
_dashboardState.value = DashboardState.Loading

try {
val (user, friends, posts) = loadAllData(userId)
_dashboardState.value = DashboardState.Success(
Dashboard(user, friends, posts)
)
} catch (e: Exception) {
_dashboardState.value = DashboardState.Error(e.message ?: "加载失败")
}
}
}

private suspend fun loadAllData(userId: String): Triple<User, List<Friend>, List<Post>> {
return coroutineScope {
val userDeferred = async { userRepository.getUser(userId) }
val friendsDeferred = async { friendsRepository.getFriends(userId) }
val postsDeferred = async { postsRepository.getPosts(userId) }

Triple(
userDeferred.await(),
friendsDeferred.await(),
postsDeferred.await()
)
}
}
}

性能对比:

  • 串行执行:3秒
  • 并发执行:1秒
  • 性能提升:67%

2.3 处理用户操作防抖

场景: 用户在搜索框输入时实时搜索,避免频繁请求。

class SearchViewModel(
private val searchRepository: SearchRepository
) : ViewModel() {

private val _searchQuery = MutableStateFlow("")

private val _searchResults = MutableStateFlow<List<SearchResult>>(emptyList())
val searchResults = _searchResults.asStateFlow()

init {
// 使用Flow操作符实现防抖
viewModelScope.launch {
_searchQuery
.debounce(300) // 300ms防抖
.distinctUntilChanged() // 去重
.filter { it.length >= 2 } // 至少2个字符才搜索
.collectLatest { query ->
searchInternal(query)
}
}
}

fun updateQuery(query: String) {
_searchQuery.value = query
}

private suspend fun searchInternal(query: String) {
try {
val results = searchRepository.search(query)
_searchResults.value = results
} catch (e: Exception) {
_searchResults.value = emptyList()
}
}
}

UI层调用:

class SearchActivity : AppCompatActivity() {
private val viewModel: SearchViewModel by viewModels()

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

binding.searchEditText.addTextChangedListener { text ->
viewModel.updateQuery(text.toString())
}

lifecycleScope.launch {
viewModel.searchResults.collect { results ->
updateSearchResults(results)
}
}
}
}

关键点:

  • 使用debounce(300)实现300ms防抖
  • distinctUntilChanged()避免重复查询
  • collectLatest确保只处理最新的搜索结果

三、网络请求的协程实践

3.1 Retrofit与协程集成

定义API接口:

interface ApiService {
@GET("users/{id}")
suspend fun getUser(@Path("id") userId: String): User

@POST("users")
suspend fun createUser(@Body user: User): User

@GET("users/{id}/posts")
suspend fun getUserPosts(
@Path("id") userId: String,
@Query("page") page: Int
): List<Post>
}

Repository层封装:

class UserRepository(
private val apiService: ApiService,
private val userDao: UserDao
) {
// 先从本地获取,再从网络更新
suspend fun getUser(userId: String): User {
return withContext(Dispatchers.IO) {
try {
// 尝试从网络获取最新数据
val user = apiService.getUser(userId)
// 缓存到本地数据库
userDao.insertUser(user)
user
} catch (e: Exception) {
// 网络失败,从本地数据库获取
userDao.getUserById(userId) ?: throw e
}
}
}

// 分页加载帖子
suspend fun getUserPosts(userId: String, page: Int): List<Post> {
return withContext(Dispatchers.IO) {
apiService.getUserPosts(userId, page)
}
}
}

3.2 处理网络错误与重试

在这里插入图片描述

实现智能重试机制:

// 重试扩展函数
suspend fun <T> retryIO(
times: Int = 3,
initialDelay: Long = 100,
maxDelay: Long = 1000,
factor: Double = 2.0,
block: suspend () -> T
): T {
var currentDelay = initialDelay
repeat(times 1) { attempt ->
try {
return block()
} catch (e: IOException) {
// 网络错误,进行重试
delay(currentDelay)
currentDelay = (currentDelay * factor).toLong().coerceAtMost(maxDelay)
}
}
return block() // 最后一次尝试,失败则抛出异常
}

// 在Repository中使用
class UserRepository(private val apiService: ApiService) {

suspend fun getUserWithRetry(userId: String): User {
return withContext(Dispatchers.IO) {
retryIO(times = 3) {
apiService.getUser(userId)
}
}
}
}

处理不同类型的网络错误:

sealed class NetworkResult<out T> {
data class Success<T>(val data: T) : NetworkResult<T>()
data class Error(val exception: Exception) : NetworkResult<Nothing>()
object Loading : NetworkResult<Nothing>()
}

suspend fun <T> safeApiCall(
apiCall: suspend () -> T
): NetworkResult<T> {
return withContext(Dispatchers.IO) {
try {
NetworkResult.Success(apiCall())
} catch (e: HttpException) {
// HTTP错误(4xx, 5xx)
NetworkResult.Error(Exception("网络请求失败: ${e.code()}"))
} catch (e: IOException) {
// 网络连接错误
NetworkResult.Error(Exception("网络连接失败"))
} catch (e: Exception) {
// 其他未知错误
NetworkResult.Error(Exception("未知错误: ${e.message}"))
}
}
}

// 在ViewModel中使用
fun loadUser(userId: String) {
viewModelScope.launch {
_userState.value = UiState.Loading

when (val result = safeApiCall { userRepository.getUser(userId) }) {
is NetworkResult.Success -> {
_userState.value = UiState.Success(result.data)
}
is NetworkResult.Error -> {
_userState.value = UiState.Error(result.exception.message ?: "未知错误")
}
is NetworkResult.Loading -> {
// 不处理
}
}
}
}

3.3 请求取消与超时

设置超时:

suspend fun getUserWithTimeout(userId: String): User {
return withContext(Dispatchers.IO) {
withTimeout(5000) { // 5秒超时
apiService.getUser(userId)
}
}
}

// 或者使用withTimeoutOrNull返回null而不是抛出异常
suspend fun getUserWithTimeoutOrNull(userId: String): User? {
return withContext(Dispatchers.IO) {
withTimeoutOrNull(5000) {
apiService.getUser(userId)
}
}
}

取消正在进行的请求:

class UserViewModel(
private val userRepository: UserRepository
) : ViewModel() {

private var loadUserJob: Job? = null

fun loadUser(userId: String) {
// 取消之前的请求
loadUserJob?.cancel()

loadUserJob = viewModelScope.launch {
_userState.value = UiState.Loading

try {
val user = userRepository.getUser(userId)
_userState.value = UiState.Success(user)
} catch (e: CancellationException) {
// 协程被取消,不处理
} catch (e: Exception) {
_userState.value = UiState.Error(e.message ?: "未知错误")
}
}
}

fun cancelLoadUser() {
loadUserJob?.cancel()
}
}


四、数据库操作的协程实践

4.1 Room与协程集成

定义DAO接口:

@Dao
interface UserDao {
@Query("SELECT * FROM users WHERE id = :userId")
suspend fun getUserById(userId: String): User?

@Query("SELECT * FROM users")
fun getAllUsersFlow(): Flow<List<User>>

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUsers(users: List<User>)

@Delete
suspend fun deleteUser(user: User)

@Query("DELETE FROM users WHERE id = :userId")
suspend fun deleteUserById(userId: String)
}

在Repository中使用:

class UserRepository(
private val userDao: UserDao,
private val apiService: ApiService
) {
// 从数据库读取用户(Flow自动在IO线程执行)
fun observeUsers(): Flow<List<User>> {
return userDao.getAllUsersFlow()
}

// 从网络获取并缓存
suspend fun refreshUsers() {
withContext(Dispatchers.IO) {
val users = apiService.getUsers()
userDao.insertUsers(users)
}
}

// 单一数据源模式(Single Source of Truth)
fun getUser(userId: String): Flow<User?> = flow {
// 1. 先发射数据库中的数据
emit(userDao.getUserById(userId))

// 2. 从网络获取最新数据
try {
val user = apiService.getUser(userId)
userDao.insertUser(user)
// 数据库更新后,Flow会自动发射新值
} catch (e: Exception) {
// 网络错误,使用数据库缓存
}
}.flowOn(Dispatchers.IO)
}

4.2 实现离线优先策略

在这里插入图片描述

完整的离线优先实现:

class PostRepository(
private val postDao: PostDao,
private val apiService: ApiService,
private val connectivityManager: ConnectivityManager
) {
// 观察帖子列表(离线优先)
fun observePosts(): Flow<List<Post>> = flow {
// 1. 立即发射本地缓存
emitAll(postDao.getAllPostsFlow())

// 2. 如果有网络,从服务器同步
if (isNetworkAvailable()) {
try {
val posts = apiService.getPosts()
postDao.insertPosts(posts)
// 数据库更新后会自动触发新的emit
} catch (e: Exception) {
// 网络同步失败,继续使用本地缓存
}
}
}.flowOn(Dispatchers.IO)

// 刷新(强制从网络加载)
suspend fun refresh(): Result<Unit> {
return withContext(Dispatchers.IO) {
try {
val posts = apiService.getPosts()
postDao.deleteAllPosts()
postDao.insertPosts(posts)
Result.success(Unit)
} catch (e: Exception) {
Result.failure(e)
}
}
}

private fun isNetworkAvailable(): Boolean {
val network = connectivityManager.activeNetwork ?: return false
val capabilities = connectivityManager.getNetworkCapabilities(network) ?: return false
return capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
}
}

在ViewModel中使用:

class PostListViewModel(
private val postRepository: PostRepository
) : ViewModel() {

val posts: StateFlow<List<Post>> = postRepository.observePosts()
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = emptyList()
)

private val _isRefreshing = MutableStateFlow(false)
val isRefreshing = _isRefreshing.asStateFlow()

fun refresh() {
viewModelScope.launch {
_isRefreshing.value = true
postRepository.refresh()
_isRefreshing.value = false
}
}
}

4.3 批量操作优化

场景: 插入大量数据时使用事务提升性能。

@Dao
interface PostDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertPosts(posts: List<Post>)

// 使用事务批量删除并插入
@Transaction
suspend fun replaceAllPosts(posts: List<Post>) {
deleteAllPosts()
insertPosts(posts)
}

@Query("DELETE FROM posts")
suspend fun deleteAllPosts()
}

// 在Repository中使用
suspend fun syncPosts() {
withContext(Dispatchers.IO) {
val posts = apiService.getPosts()
postDao.replaceAllPosts(posts) // 事务中执行,保证原子性
}
}


五、UI更新与线程切换

5.1 Dispatchers详解

Kotlin协程提供了四种调度器:

Dispatcher用途线程池
Dispatchers.Main UI更新、轻量级操作 主线程
Dispatchers.IO 网络、文件、数据库 共享线程池(最多64线程)
Dispatchers.Default CPU密集型计算 线程数=CPU核心数
Dispatchers.Unconfined 不限制线程(不推荐) 调用者线程

最佳实践:

viewModelScope.launch {
// 在主线程启动协程

val user = withContext(Dispatchers.IO) {
// 切换到IO线程执行网络请求
apiService.getUser(userId)
}
// 自动切回主线程

// 更新UI(在主线程)
binding.userName.text = user.name
}

5.2 避免不必要的线程切换

❌ 过度切换:

suspend fun loadData() {
withContext(Dispatchers.IO) {
val data = apiService.getData()

withContext(Dispatchers.Main) {
// 不必要的切换
updateUI(data)
}
}
}

✅ 优化后:

suspend fun loadData() {
val data = withContext(Dispatchers.IO) {
apiService.getData()
}
// 自动在主线程
updateUI(data)
}

5.3 在Activity/Fragment中收集Flow

使用lifecycleScope与repeatOnLifecycle:

class UserProfileFragment : Fragment() {
private val viewModel: UserViewModel by viewModels()

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

// ✅ 推荐:生命周期感知
viewLifecycleOwner.lifecycleScope.launch {
viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
// 只在STARTED状态收集Flow
viewModel.userState.collect { state ->
when (state) {
is UiState.Loading -> showLoading()
is UiState.Success -> showUser(state.data)
is UiState.Error -> showError(state.message)
}
}
}
}
}
}

为什么使用repeatOnLifecycle(STARTED):

  • Fragment在后台时停止收集,节省资源
  • Fragment回到前台时自动恢复收集
  • 避免内存泄漏和不必要的UI更新

六、异常处理与错误传播

6.1 协程中的异常类型

两种异常类型:

  • CancellationException: 协程取消,不会传播
  • 其他异常: 会传播到父协程,导致整个协程树取消
  • 示例:

    viewModelScope.launch {
    launch {
    delay(100)
    throw RuntimeException("子协程异常")
    // 这个异常会传播到父协程,导致viewModelScope取消
    }

    launch {
    delay(200)
    println("这行代码不会执行") // 父协程已被取消
    }
    }

    6.2 使用SupervisorJob隔离异常

    SupervisorJob: 子协程异常不会影响其他子协程。

    val supervisorScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)

    supervisorScope.launch {
    launch {
    delay(100)
    throw RuntimeException("子协程1异常")
    // 这个异常不会影响子协程2
    }

    launch {
    delay(200)
    println("子协程2继续执行") // 会执行
    }
    }

    在ViewModel中使用SupervisorJob:

    class RobustViewModel : ViewModel() {
    private val supervisorJob = SupervisorJob()
    private val scope = CoroutineScope(supervisorJob + Dispatchers.Main)

    fun loadMultipleData() {
    scope.launch {
    // 启动多个独立的子任务
    launch {
    try {
    loadUserData()
    } catch (e: Exception) {
    handleError("用户数据加载失败", e)
    }
    }

    launch {
    try {
    loadPostsData()
    } catch (e: Exception) {
    handleError("帖子数据加载失败", e)
    }
    }
    }
    }

    override fun onCleared() {
    super.onCleared()
    supervisorJob.cancel()
    }
    }

    6.3 全局异常处理

    设置CoroutineExceptionHandler:

    val exceptionHandler = CoroutineExceptionHandler { _, exception ->
    Log.e("Coroutine", "捕获到未处理的异常", exception)
    // 上报到崩溃收集平台(如Firebase Crashlytics)
    }

    val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main + exceptionHandler)

    scope.launch {
    throw RuntimeException("未捕获的异常")
    // 会被exceptionHandler捕获
    }

    在Application中设置全局处理器:

    class MyApplication : Application() {
    override fun onCreate() {
    super.onCreate()

    // 设置全局协程异常处理器
    Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
    Log.e("GlobalHandler", "未捕获的异常", throwable)
    // 上报崩溃
    FirebaseCrashlytics.getInstance().recordException(throwable)
    }
    }
    }


    七、性能优化与常见陷阱

    7.1 避免内存泄漏

    ❌ 陷阱1: 使用GlobalScope

    class LeakyActivity : AppCompatActivity() {
    fun loadData() {
    GlobalScope.launch {
    // 危险!Activity销毁后协程仍在运行
    val data = fetchData()
    updateUI(data) // 可能访问已销毁的Activity
    }
    }
    }

    ✅ 正确方式: 使用lifecycleScope

    class SafeActivity : AppCompatActivity() {
    fun loadData() {
    lifecycleScope.launch {
    // Activity销毁时自动取消
    val data = fetchData()
    updateUI(data)
    }
    }
    }

    ❌ 陷阱2: 静态引用持有Context

    companion object {
    var coroutineJob: Job? = null // 危险!
    }

    fun loadData(context: Context) {
    coroutineJob = GlobalScope.launch {
    // context可能是Activity,导致内存泄漏
    val data = fetchData(context)
    }
    }

    ✅ 正确方式: 使用Application Context

    fun loadData(context: Context) {
    val appContext = context.applicationContext

    viewModelScope.launch {
    val data = fetchData(appContext)
    }
    }

    7.2 控制并发数量

    问题: 同时发起1000个网络请求导致OOM。

    ✅ 解决方案: 使用Semaphore限流

    import kotlinx.coroutines.sync.Semaphore
    import kotlinx.coroutines.sync.withPermit

    class ImageLoader {
    private val semaphore = Semaphore(5) // 最多5个并发

    suspend fun loadImages(urls: List<String>): List<Bitmap> {
    return urls.map { url ->
    async {
    semaphore.withPermit {
    // 最多同时执行5个
    loadImage(url)
    }
    }
    }.awaitAll()
    }

    private suspend fun loadImage(url: String): Bitmap {
    // 下载图片
    return withContext(Dispatchers.IO) {
    // 实际下载逻辑
    TODO()
    }
    }
    }

    7.3 使用Channel避免背压问题

    场景: 生产者速度 >> 消费者速度,导致内存占用暴涨。

    ❌ 使用Flow(无背压控制)

    fun produceData(): Flow<Data> = flow {
    repeat(1_000_000) {
    emit(Data(it)) // 快速发射数据
    }
    }

    fun consumeData() {
    viewModelScope.launch {
    produceData().collect { data ->
    delay(10) // 消费慢
    process(data)
    }
    }
    }

    ✅ 使用Channel(有容量限制)

    val channel = Channel<Data>(capacity = 100) // 缓冲100个元素

    fun produceData() {
    viewModelScope.launch {
    repeat(1_000_000) {
    channel.send(Data(it)) // 缓冲满时挂起
    }
    channel.close()
    }
    }

    fun consumeData() {
    viewModelScope.launch {
    for (data in channel) {
    delay(10)
    process(data)
    }
    }
    }

    7.4 避免阻塞Dispatchers.IO线程池

    ❌ 在IO线程池执行CPU密集型任务

    suspend fun processLargeData(data: List<Int>) {
    withContext(Dispatchers.IO) {
    // CPU密集型计算,占用IO线程
    data.map { it * it * it }.sorted()
    }
    }

    ✅ 使用Dispatchers.Default

    suspend fun processLargeData(data: List<Int>) {
    withContext(Dispatchers.Default) {
    // CPU密集型任务使用Default
    data.map { it * it * it }.sorted()
    }
    }


    八、总结与最佳实践清单

    核心要点回顾

  • 使用生命周期感知的作用域

    • viewModelScope for ViewModel
    • lifecycleScope for Activity/Fragment
    • 避免使用GlobalScope
  • 线程切换

    • Dispatchers.Main – UI操作
    • Dispatchers.IO – 网络、文件、数据库
    • Dispatchers.Default – CPU密集型计算
  • 错误处理

    • 使用try-catch处理已知异常
    • 使用SupervisorJob隔离子协程异常
    • 设置CoroutineExceptionHandler全局捕获
  • 性能优化

    • 使用async并发执行独立任务
    • 使用Semaphore控制并发数量
    • 避免在循环中创建协程
  • 数据流管理

    • 使用StateFlow替代LiveData
    • 使用repeatOnLifecycle收集Flow
    • 实现离线优先策略
  • 最佳实践检查清单

    作用域管理:

    • 使用viewModelScope在ViewModel中启动协程
    • 使用lifecycleScope在Activity/Fragment中启动协程
    • 避免使用GlobalScope
    • 在自定义作用域时使用SupervisorJob

    线程调度:

    • 网络请求使用Dispatchers.IO
    • 数据库操作使用Dispatchers.IO
    • CPU密集型计算使用Dispatchers.Default
    • UI更新在Dispatchers.Main

    异常处理:

    • 在协程顶层使用try-catch
    • 区分CancellationException和其他异常
    • 使用CoroutineExceptionHandler记录未捕获异常
    • 在Repository层捕获并转换异常

    性能优化:

    • 使用async并发执行独立任务
    • 使用Semaphore限制并发数量
    • 避免阻塞Dispatcher线程池
    • 使用Flow.buffer()控制背压

    测试:

    • 使用runTest测试suspend函数
    • 使用StandardTestDispatcher控制测试时间
    • Mock Repository和API
    • 测试不同的UI状态

    学习资源

    官方文档:

    • Kotlin协程官方指南: https://kotlinlang.org/docs/coroutines-guide.html
    • Android协程最佳实践: https://developer.android.com/kotlin/coroutines/coroutines-best-practices

    推荐阅读:

    • 《Kotlin Coroutines by Tutorials》
    • 《Asynchronous Programming with Kotlin》
    • Roman Elizarov的博客(协程库作者)

    视频教程:

    • Android Developers YouTube频道 – Coroutines系列
    • Kotlin Conf演讲 – Coroutines相关主题

    系列文章导航:

    • 👉 上一篇: 性能优化:内联、内存与字节码分析

    如果这篇文章对你有帮助,欢迎点赞、收藏、分享!有任何问题或建议,欢迎在评论区留言讨论。让我们一起学习,一起成长!

    也欢迎访问我的个人主页发现更多宝藏资源

    赞(0)
    未经允许不得转载:171主机测评 » 【Kotlin系列17】协程在Android中的最佳实践:从入门到精通
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址