Kotlin语言进阶:协程、Flow、Channel详解(一)
Kotlin语言进阶:协程、Flow、Channel详解(一)
一、协程基础概念
1.1 什么是协程
协程(Coroutines)是Kotlin提供的一种轻量级线程的实现方案,它可以在单线程中模拟多线程编程的效果。相比传统的线程,协程具有以下优势:
- 轻量级:启动成本更低
- 内存占用少:可以同时运行大量协程
- 内置取消支持:可以自动取消整个协程层次结构
- 结构化并发:父协程可以控制子协程的生命周期
1.2 基本使用
// 启动一个协程
GlobalScope.launch {
delay(1000L) // 非阻塞的等待1秒钟
println("World!") // 在延迟后打印
}
print("Hello, ") // 主线程继续执行
Thread.sleep(2000L) // 阻塞主线程2秒钟来保证JVM存活
二、协程作用域
2.1 CoroutineScope
CoroutineScope定义了协程的作用范围,主要有以下几种:
// 1. GlobalScope:全局作用域,生命周期与应用程序相同
GlobalScope.launch {
// 协程代码
}
// 2. coroutineScope:创建一个新的作用域
suspend fun doSomething() = coroutineScope {
launch {
// 子协程1
}
launch {
// 子协程2
}
}
// 3. Android中的viewModelScope
class MyViewModel : ViewModel() {
init {
viewModelScope.launch {
// 协程会在ViewModel清除时自动取消
}
}
}
2.2 作用域构建器
// runBlocking:阻塞当前线程直到内部协程完成
fun main() = runBlocking {
launch {
delay(1000L)
println("World!")
}
println("Hello")
}
// supervisorScope:子协程失败不会影响其他子协程
suspend fun supervisedFunc() = supervisorScope {
val first = async { doFirstThing() }
val second = async { doSecondThing() }
first.await() + second.await()
}
三、协程调度器
3.1 Dispatchers类型
CoroutineScope(Dispatchers.Main).launch {
// UI线程
}
CoroutineScope(Dispatchers.IO).launch {
// IO操作线程池
}
CoroutineScope(Dispatchers.Default).launch {
// CPU密集型操作线程池
}
CoroutineScope(Dispatchers.Unconfined).launch {
// 不限制线程
}
3.2 调度器切换
CoroutineScope(Dispatchers.Main).launch {
// 主线程
val result = withContext(Dispatchers.IO) {
// IO线程执行
api.getData()
}
// 回到主线程
updateUI(result)
}
四、实战案例
4.1 网络请求处理
class UserRepository {
suspend fun getUserData(): User = withContext(Dispatchers.IO) {
try {
val response = api.getUser() // 网络请求
response.toUser() // 数据转换
} catch (e: Exception) {
throw UserException("获取用户数据失败")
}
}
}
class UserViewModel : ViewModel() {
private val _user = MutableLiveData<User>()
val user: LiveData<User> = _user
fun loadUser() {
viewModelScope.launch {
try {
val result = userRepository.getUserData()
_user.value = result
} catch (e: Exception) {
// 错误处理
}
}
}
}
4.2 并行请求处理
suspend fun loadData() = coroutineScope {
val users = async { repository.getUsers() }
val products = async { repository.getProducts() }
val combinedData = CombinedData(
users = users.await(),
products = products.await()
)
}
五、其他
5.1 协程的启动模式
// 立即启动
launch(start = CoroutineStart.DEFAULT) { }
// 懒加载启动
val job = launch(start = CoroutineStart.LAZY) { }
job.start() // 手动启动
// 原子操作启动
launch(start = CoroutineStart.ATOMIC) { }
// 撤销启动
launch(start = CoroutineStart.UNDISPATCHED) { }
5.2 异常处理
viewModelScope.launch {
try {
val result = withContext(Dispatchers.IO) {
// 可能抛出异常的操作
api.riskyOperation()
}
handleSuccess(result)
} catch (e: Exception) {
handleError(e)
} finally {
// 清理工作
}
}
六、面试题解析
6.1 基础概念
Q:协程与线程的区别是什么?
A:
- 协程是轻量级的,启动成本更低(几kb内存 vs 几mb内存)
- 协程支持结构化并发,更容易管理
- 协程提供了内置的取消支持
- 协程可以在单线程上模拟多线程操作
Q:什么是挂起函数?
A:
- 挂起函数是可以暂停执行并稍后恢复的函数
- 使用suspend关键字标记
- 只能在协程或其他挂起函数中调用
- 不会阻塞线程
6.2 实践应用
Q:如何在Android中正确使用协程?
A:
- 使用适当的作用域(lifecycleScope/viewModelScope)
- 正确处理异常
- 合理选择调度器
- 注意内存泄漏
Q:协程的取消机制是如何工作的?
A:
- 通过Job对象控制协程生命周期
- 取消是协作的,需要协程代码检查取消状态
- withContext和yield函数会自动检查取消状态
- 可以使用try-finally确保资源释放
七、开源项目实战
7.1 Retrofit协程支持
interface ApiService {
@GET("users")
suspend fun getUsers(): List<User>
}
class Repository {
private val api = retrofit.create(ApiService::class.java)
suspend fun getUsers(): List<User> = withContext(Dispatchers.IO) {
api.getUsers()
}
}
7.2 Room数据库协程操作
@Dao
interface UserDao {
@Query("SELECT * FROM users")
suspend fun getAll(): List<User>
@Insert
suspend fun insert(user: User)
}
八、调试技巧
8.1 协程调试
// 添加调试信息
val job = launch(CoroutineName("MyCoroutine")) {
log.d("协程开始执行")
// ...
}
// 使用Debug模式
System.setProperty("kotlinx.coroutines.debug", "on")
8.2 异常追踪
CoroutineExceptionHandler { context, exception ->
println("Caught $exception")
exception.printStackTrace()
}
九、总结
本文介绍了Kotlin协程的基础概念和实践应用:
- 协程的基本概念和优势
- 协程作用域和构建器的使用
- 不同类型的调度器及其应用场景
- 实战案例和性能优化建议
- 常见面试题解析
在实际开发中,建议:
- 合理使用协程作用域
- 正确处理异常情况
- 选择合适的调度器
- 注意性能优化
下一篇文章将深入介绍Flow和Channel的使用。