我们这里只讲解一下,协程在Android项目中常见用法,原理知识不在进行说明了。
依赖
lifecycleScope只能在Activity、Fragment中使用,会绑定Activity和Fragment的生命周期。依赖库:
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.5.0'
viewModelScope 只能在ViewModel中使用,绑定ViewModel的生命周期。依赖库:
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.0'
场景一
lifecycleScope.launch {
println("MainActivity.onCreate 开始")
for (i in 0..5) {
queryAdministrativeBoundaries()
}
println("MainActivity.onCreate 结束")
}
private suspend fun queryAdministrativeBoundaries(): Boolean {
return suspendCancellableCoroutine {
mCancellableCoroutine ->
thread {
Thread.sleep(3000)
println("MainActivity.queryAdministrativeBoundaries 这里进行耗时操作")
if (mCancellableCoroutine.isActive) {
mCancellableCoroutine.resume(true)
}
}
}
}
场景二
并发多线程需求处理:
/**
* 多线程并发处理
* kotlin 为我们提供了Mutex实现线程安全,Mutex通俗点来说就是kotlin的锁,和java 的synchronized和RecentLock对应。
* 使用mutex.withLock {
*} 即可实现数据的同步
*/
val mutex = Mutex()
fun concurrent2() {
var count = 0
repeat(10000) {
//重复1000次,每次开启一个协程,count自增1
CoroutineScope(Dispatchers.Default).launch {
mutex.withLock {
count++
}
println("中间值:$count")
}
}
}
并发单线程需求处理:
/**
* 创建协程作用域,使用 Unconfined,这样在协程被挂起前都不会改变线程,也就是说协程始终运行在单线程中
*/
fun singleThread() {
val scope = CoroutineScope(Dispatchers.Unconfined)
var count = 0
repeat(1000) {
//重复1000次,每次开启一个协程,count自增1
scope.launch {
println("线程id:${Thread.currentThread().id}")//这个线程始终不会变,除非你在这里挂起
count++
println(count)
}
}
}
基本知识:
不管是指定协程的运行线程,还是临时切换线程,运行完毕会切换回来,都是通过 Dispatchers 来调度的。常用的线程调度为:
- Dispatchers.Main Android主线程
- Dispatchers.Unconfined 当前CoroutineScope的线程策略
- Dispatchers.Default 默认值,为JVM共享线程池
- Dispatchers.IO IO线程池,默认为64个线程
GlobalScope,还有要一些常见的CoroutineScope对象:
- lifecycleScope 生命周期范围内,用于Activity组件
- coroutineScope{} 一个suspend方法,创建一个新的作用域,并在该作用域内执行指定代码块,它并不启动协程。
- runBlocking{} 是一个裸方法,创建一个协程,并阻塞当前线程,直到协程执行完毕。前面说过,这里不再赘述。
- runBlocking 与 coroutineScope 的主要区别在于后者在等待所有子协程执行完毕时不会阻塞当前线程。
- withContext(){}一个suspend方法,在给定的上下文执行给定挂起块并返回结果,它并不启动协程,只会(可能会)导致线程的切换。用它执行的挂起块中的上下文是当前协程的上下文和由它执行的上下文的合并结果。withContext的目的不在于启动子协程,它最初用于将长耗时操作从UI线程切走,完事再切回来。
- suspend挂起函数是不会阻塞线程的,它只会挂起协程,而不阻塞线程。
- async是异步执行,withContext是同步执行。
参考文章:
https://www.cnblogs.com/kevin2022/p/16637415.html