协程:协程的目的是为了让多个任务之间更好的协作,解决异步回调嵌,能够以同步的方式编排代码完成异步工作。将异步代码像同步代码一样直观。同时它也是一个并发流程控制的解决方案。
1.怎么启动协程
协程的启动如下
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
}
public fun <T> CoroutineScope.async(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> T
): Deferred<T> {
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyDeferredCoroutine(newContext, block) else
DeferredCoroutine<T>(newContext, active = true)
coroutine.start(start, coroutine, block)
return coroutine
}
常见的协程创建方法有
launch是一个CoroutineScope的扩展方法在不阻塞当前线程的情况下启动一个新的协程,并返回Job对象。job对象可以看作协程本身,包含了对协程的控制方法
async方法返回Deffered是job的子类,实际上就是增加了awiat方法,能够让当前协程暂时挂起,暂停往下执行,当await方法有返回值后,会恢复协程,继续往下执行。
Deffered和job都有的方法有
start()手动启动协程
join()等待协程执行完毕
cancel()取消一个协程
CoroutineScope是一个接口,我们通常使用它的实现类GlobalScope这是一个全局的变量。上述方法中第一个参数context是协程的上下文如果我们不传默认会使用EmptyCoroutineContext,经过 newCoroutineContext(context)会返回Default协程运行调度器。
Disaptchers.IO 显示的指定协程运行的线程为IO线程
Dispathcers.Main 指定这个协程运行在主线程上。
Dispathcers.Default 默认的,启动协程时会启动一个线程
Dispathcers.Unconfined 不指定,在当前线程运行,协程恢复后的运行的线程取决于协程关起时所在的线程。
在协程调度器中needDispatch()方法表示是否需要将协程代码块分发到相应的线程中,其中只有Unconfined不需要分发,因为他是运行在当前线程中。
第二个参数是协程启动的类型
DEFAULT模式表示创建即启动,可随时取消。
ATOMIC自动模式,创建即启动,但是启动前不可取消。
LAZY模式只用当调用start方法后才会启动。
最后一个参数则是我们的闭包代码块
我们来看一下代码
private val TAG = "CoroutineScene"
private suspend fun request1() : String{
delay(2000)
Log.e(TAG, "request1 work on Thread ${Thread.currentThread().name}")
return "request1"
}
private suspend fun request2() : String{
delay(2000)
Log.e(TAG, "request2 work on Thread ${Thread.currentThread().name}")
return "request2"
}
private suspend fun request3(str : String = "") : String{
delay(2000)
Log.e(TAG, "request3 work on Thread ${Thread.currentThread().name}")
return "request3"
}
/**
* 这种写法异步任务按书写顺序执行
2022-09-02 15:18:38.641 6163-6163/com.example.tdframe E/CoroutineScene: coroutine1
2022-09-02 15:18:38.641 6163-6163/com.example.tdframe E/CoroutineScene: run coroutine1
2022-09-02 15:18:40.645 6163-6163/com.example.tdframe E/CoroutineScene: request1 work on Thread main
2022-09-02 15:18:42.649 6163-6163/com.example.tdframe E/CoroutineScene: request2 work on Thread main
2022-09-02 15:18:44.653 6163-6163/com.example.tdframe E/CoroutineScene: request3 work on Thread main
2022-09-02 15:18:44.653 6163-6163/com.example.tdframe E/CoroutineScene: updateUI r1=request1, r2 = request2, r3 = request3 Thread main
代码是运行在UI线程上的,延迟代码运行在IO线程
*/
fun coroutine1(){
GlobalScope.launch(context = Dispatchers.Main) {
Log.e(TAG, "run coroutine1")
val r1 = request1()
val r2 = request2()
val r3 = request3(r2)
updateUI(r1, r2, r3)
}
Log.e(TAG, "coroutine1")
}
上面的代码使用的协程调度器为 Dispatchers.Main从上面的日志中可以看到Log.e(TAG, “coroutine1”)闭包代码块之外的代码是在之前被低昂用的,因为 Dispatchers.Main最后是通handler让协程运行在主线程上。
/**
2022-09-02 15:36:04.386 6163-6163/com.example.tdframe E/CoroutineScene: coroutine1
2022-09-02 15:36:04.386 6163-6443/com.example.tdframe E/CoroutineScene: run coroutine1, ThreadDefaultDispatcher-worker-1
2022-09-02 15:36:06.388 6163-6443/com.example.tdframe E/CoroutineScene: request1 work on Thread DefaultDispatcher-worker-1
2022-09-02 15:36:08.389 6163-6443/com.example.tdframe E/CoroutineScene: request2 work on Thread DefaultDispatcher-worker-1
2022-09-02 15:36:10.392 6163-6443/com.example.tdframe E/CoroutineScene: request3 work on Thread DefaultDispatcher-worker-1
2022-09-02 15:36:10.392 6163-6443/com.example.tdframe E/CoroutineScene: updateUI r1=request1, r2 = request2, r3 = request3 Thread DefaultDispatcher-worker-1
*/
fun coroutine1(){
GlobalScope.launch(context = Dispatchers.Default) {
Log.e(TAG, "run coroutine1, Thread${Thread.currentThread().name}")
val r1 = request1()
val r2 = request2()
val r3 = request3(r2)
updateUI(r1, r2, r3)
}
Log.e(TAG, "coroutine1")
}
这是调度器为Dispatchers.Default代码运行在自带的线程中。
/**
2022-09-02 15:42:20.969 6163-6163/com.example.tdframe E/CoroutineScene: run coroutine1, Threadmain
2022-09-02 15:42:20.969 6163-6163/com.example.tdframe E/CoroutineScene: coroutine1
2022-09-02 15:42:22.972 6163-6705/com.example.tdframe E/CoroutineScene: request1 work on Thread kotlinx.coroutines.DefaultExecutor
2022-09-02 15:42:24.974 6163-6705/com.example.tdframe E/CoroutineScene: request2 work on Thread kotlinx.coroutines.DefaultExecutor
2022-09-02 15:42:26.976 6163-6705/com.example.tdframe E/CoroutineScene: request3 work on Thread kotlinx.coroutines.DefaultExecutor
2022-09-02 15:42:26.976 6163-6705/com.example.tdframe E/CoroutineScene: updateUI r1=request1, r2 = request2, r3 = request3 Thread kotlinx.coroutines.DefaultExecutor
*/
fun coroutine1(){
GlobalScope.launch(context = Dispatchers.Unconfined) {
Log.e(TAG, "run coroutine1, Thread${Thread.currentThread().name}")
val r1 = request1()
val r2 = request2()
val r3 = request3(r2)
updateUI(r1, r2, r3)
}
Log.e(TAG, "coroutine1")
}
这是调度器为Dispatchers.Unconfined代码运行的情况,
Log.e(TAG, “run coroutine1, Thread${Thread.currentThread().name}”)这句代码因为是在主线程上调用的所有打印出来是在MainThread上,但是request1中的代码为什么没有运行在住线程中呢
private suspend fun request1() : String{
delay(2000)
Log.e(TAG, "request1 work on Thread ${Thread.currentThread().name}")
return "request1"
}
因为在request1中我们调用了delay方法,delay方法是在一个默认的线程池中调用,代码执行到delay协程将会被挂起,之后延时结束后将会被恢复,以回调的方式执行接下来的代码。
关于协程的挂起和恢复我将在下一章讲解。
对于Android,jetPack建议我们使用如下方式使用协程,和宿主的生命周期绑定
/**
* E/CoroutineScene: =================onCreate===================
E/CoroutineScene: lifecycleScope.launchWhenCreated:whenCreated
E/CoroutineScene: lifecycle.coroutineScope.launchWhenCreated:whenCreated
E/CoroutineScene: =================onStart===================
E/CoroutineScene: lifecycleScope.launchWhenStarted:whenCreated
E/CoroutineScene: lifecycle.coroutineScope.launchWhenStarted:whenCreated
E/CoroutineScene: =================onResume===================
E/CoroutineScene: lifecycleScope.launchWhenResumed:whenCreated
E/CoroutineScene: lifecycleScope.launchWhenResumed:whenResumed
lifecycleScope.launchWhenResumed:whenStarted
E/CoroutineScene: lifecycle.coroutineScope.launchWhenResumed:whenCreated
lifecycle.coroutineScope.launchWhenResumed:whenResumed
lifecycle.coroutineScope.launchWhenResumed:whenStarted
E/CoroutineScene: lifecycleScope.launchWhenCreated:whenResumed
lifecycleScope.launchWhenCreated:whenStarted
E/CoroutineScene: lifecycle.coroutineScope.launchWhenCreated:whenResumed
lifecycle.coroutineScope.launchWhenCreated:whenStarted
lifecycleScope.launchWhenStarted:whenResumed
E/CoroutineScene: lifecycleScope.launchWhenStarted:whenStarted
lifecycle.coroutineScope.launchWhenStarted:whenResumed
E/CoroutineScene: lifecycle.coroutineScope.launchWhenStarted:whenStarted
*/
lifecycleScope.launchWhenCreated {
whenCreated {
Log.e("CoroutineScene", "lifecycleScope.launchWhenCreated:whenCreated")
}
whenResumed {
Log.e("CoroutineScene", "lifecycleScope.launchWhenCreated:whenResumed")
}
whenStarted {
Log.e("CoroutineScene", "lifecycleScope.launchWhenCreated:whenStarted")
}
}
lifecycle.coroutineScope.launchWhenCreated {
whenCreated {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenCreated:whenCreated")
}
whenResumed {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenCreated:whenResumed")
}
whenStarted {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenCreated:whenStarted")
}
}
lifecycleScope.launchWhenResumed {
whenStarted {
Log.e("CoroutineScene", "lifecycleScope.launchWhenResumed:whenCreated")
}
whenResumed {
Log.e("CoroutineScene", "lifecycleScope.launchWhenResumed:whenResumed")
}
whenStarted {
Log.e("CoroutineScene", "lifecycleScope.launchWhenResumed:whenStarted")
}
}
lifecycle.coroutineScope.launchWhenResumed {
whenStarted {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenResumed:whenCreated")
}
whenResumed {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenResumed:whenResumed")
}
whenStarted {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenResumed:whenStarted")
}
}
lifecycleScope.launchWhenStarted {
whenStarted {
Log.e("CoroutineScene", "lifecycleScope.launchWhenStarted:whenCreated")
}
whenResumed {
Log.e("CoroutineScene", "lifecycleScope.launchWhenStarted:whenResumed")
}
whenStarted {
Log.e("CoroutineScene", "lifecycleScope.launchWhenStarted:whenStarted")
}
}
lifecycle.coroutineScope.launchWhenStarted {
whenStarted {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenStarted:whenCreated")
}
whenResumed {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenStarted:whenResumed")
}
whenStarted {
Log.e("CoroutineScene", "lifecycle.coroutineScope.launchWhenStarted:whenStarted")
}
}
}