从这篇文章开始,就进入了协程的学习阶段了,但是由于协程的学习是一个比较系统性的东西,他有很多需要了解的知识点 ,比如 suspend挂起 协程作用域Scope 协程体 调度器Dispatchers 等,当知识积累到一定程度再去阅读源码可能效果会更好一点,所以从协程这个板块,我打算从最简单的例子说起
下面上代码
kotlin
object TsmTest {
@JvmStatic
fun main(arge: Array<String>) {
GlobalScope.launch(Dispatchers.IO) {
Thread.sleep(100)
println("--------result----------${Thread.currentThread().name}")
}
println("--------方法结束----------")
Thread.sleep(1000)
}
}
这个是一个特别简单的方法,可以直接Run ,不必去打包android 就可以直接运行, 调试起来也比较简单
在这里有几个比较重要的知识点
CoroutineScope 作用域
在 android 中非常多的应用场景都有生命周期的这个概念,稍微不注意就会造成内存泄露的麻烦,但是如果去维护生命周期就会造成代码的臃肿和难以阅读,在这种场景下作用域的功能就体现出来了,
下面列举一下比较常用的作用域
1:如果你是在ViewModule 中使用协程可以使用 androidx.lifecycle:lifecycle-viewmodel-ktx 包下面为我们提供的 viewModelScope ,
viewModelScope 的默认调度器是 Dispatchers.Main
kotlin
public val ViewModel.viewModelScope: CoroutineScope
get() {
val scope: CoroutineScope? = this.getTag(JOB_KEY)
if (scope != null) {
return scope
}
return setTagIfAbsent(
JOB_KEY,
CloseableCoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)
)
}
2:如果你是在Activity 或者Fragment 中使用可以使用 androidx.lifecycle:lifecycle-runtime-ktx 为我们提供的 lifecycleScope,让协程中的任务自动感知他们所处的环境,当宿主被回收后,任务自动与宿主隔离,
lifecycleScope 的默认调度器是 Dispatchers.Main
kotlin
public val Lifecycle.coroutineScope: LifecycleCoroutineScope
get() {
while (true) {
val existing = mInternalScopeRef.get() as LifecycleCoroutineScopeImpl?
if (existing != null) {
return existing
}
val newScope = LifecycleCoroutineScopeImpl(
this,
SupervisorJob() + Dispatchers.Main.immediate
)
if (mInternalScopeRef.compareAndSet(null, newScope)) {
newScope.register()
return newScope
}
}
}
3:GlobalScope 他的作用域就是 App 的整个生命周期,所以也可以说他没有作用域
GlobalScope 的默认调度器是 Dispatchers.Default
我们协程体中是可以获取到CoroutineScope的,在协程体中可以直接使用原因我们就需要看一下 launch 方法
kotlin
public fun CoroutineScope.launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
CoroutineScope.launch 方法一共有3个参数,我们先不管前面2个参数,直接看第三个参数 ,他是一个被 suspend 关键字修饰的 block,同样的这个block 还是 CoroutineScope扩展的一个匿名函数,
根据上面的分析我们就可以将 上面代码改写成
kotlin
var job= GlobalScope.launch(block = {
Thread.sleep(100)
println("--------result----------${Thread.currentThread().name}")
this.cancel()
this.isActive
})
图片中的两个this 代表的是样的含义,都是 CoroutineScope,既然 block 是 CoroutineScope 的匿名扩展方法,我们就可以使用这个 this 来取消或者得到当前协程的结果
关于 CoroutineScope 还有一些知识点,比如异常的传播,取消机制 等等,我们后边继续分析
launch async
GlobalScope.launch 会开启一段协程,但是他会返回一个 Job , 我们可以根据这个job 来获取和操控任务的状态,但是他没有结果,也就是无法通过 job获取结果,但是如果你要结果该如何使用呢. 没错 就是使用 async 方法
GlobalScope.async 会返回一个 Deferred 的对象,我们可以用 Deferred.await 方法来获取 协程的返回结果,需要了解到的是 Deferred 他是 Job 的子类
Dispatchers.IO 调度器
调度器决定了协程体中的代码的执行时所挂载到的线程, 默认是 Dispatchers.Default ,但是这里面有一个问题,那就是 Dispatchers.IO 与 Dispatchers.Default 有什么不同吗,下面就把 源码中 Dispatchers.IO 的注释拿出来
vbnet
/**
* The [CoroutineDispatcher] that is designed for offloading blocking IO tasks to a shared pool of threads.
*
* Additional threads in this pool are created and are shutdown on demand.
* The number of threads used by tasks in this dispatcher is limited by the value of
* "`kotlinx.coroutines.io.parallelism`" ([IO_PARALLELISM_PROPERTY_NAME]) system property.
* It defaults to the limit of 64 threads or the number of cores (whichever is larger).
*
* Moreover, the maximum configurable number of threads is capped by the
* `kotlinx.coroutines.scheduler.max.pool.size` system property.
* If you need a higher number of parallel threads,
* you should use a custom dispatcher backed by your own thread pool.
*
* ### Implementation note
*
* This dispatcher shares threads with a [Default][Dispatchers.Default] dispatcher, so using
* `withContext(Dispatchers.IO) { ... }` does not lead to an actual switching to another thread —
* typically execution continues in the same thread.
* As a result of thread sharing, more than 64 (default parallelism) threads can be created (but not used)
* during operations over IO dispatcher.
*/
@JvmStatic
public val IO: CoroutineDispatcher = DefaultScheduler.IO
这段话的意思就是 DefaultScheduler.IO 是为了加载一些 IO 任务的共享线程池,他确定了最大线程数, 但是从线程意义与 Dispatchers.Default 上来说是没有区别的,如果当前协程体的调度器是 Dispatchers.Default ,如果你使用 withContext(Dispatchers.IO) ,那么他不会进行线程调度,
用比较通俗的话来讲就是 Dispatchers.Default 是运算密集型, 而 Dispatchers.IO 是任务密集型 ,但是这两个调度器之间不能来回切换,没有什么太大的意义