1.概述
协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。
2.协成特点
协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括一下几点:
- 轻量:可以在单个线程上运行多个线程,由于协成支持挂起,不会使正在运行协成的线程阻塞,挂起比阻塞节省内存。
- 内置取消协成支持:我们可以在协成运行前取消协成。
3.协成使用
3.1.添加依赖
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9")
}
3.2.协程构建器种类
- 3.2.1.GlobalScope.launch
特点:全局的协程构建器,并不会阻塞当前线程。它的生命周期随着Application。一般不建议使用。
注意: 协程是依附于线程的,一旦线程执行完成,协程也就结束了,不会执行了。
//1.全局的协程构建器,并不会阻塞当前线程。
GlobalScope.launch {
//给定一个延时的时间,同时它不会阻塞线程,会在一定时间后执行协程。
delay(1000)
println("Kotlin Coroutines")
}
- 3.2.2.runBlocking
特点:协程构建器,它会阻塞当前线程,直到协程执行结束。(一般用于main函数和测试使用)
//它会创建新的协程,并阻塞当前线程。
runBlocking {
delay(2000)
}
- 3.2.3.coroutineScope
特点:它会创建一个新的协程,他并不会阻塞当前线程,并会等待协程执行完成。
注意:它是一个挂起函数suspend
coroutineScope {
launch {
delay(20000)
println("my job2")
}
delay(5000)
println("hello world")
}
- 3.2.4.lifecycleScope/viewModelScope
注意:
1.lifecycleScope只能在Activity或者Fragment中使用,他会依附与Activity/Fragmen的声明周期。页面销毁,协程也就被销毁了,避免内存泄漏。
2.viewModelScope只能在ViewModel的子类中使用,他也是随着ViewModel生命周期的,ViewModel销毁了,协程也就销毁了。
需要添加依赖
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version")
//1.lifecycleScope的使用
lifecycleScope.launch {}
lifecycleScope.async {}
//当我们宿主的声明周期至少为onCreate的时候才会启动
lifecycleScope.launchWhenCreated {
HiLog.e(TAG,"launchWhenCreated")
whenCreated {
//这里的代码 只有宿主的声明周期为onCreate才会执行,否则暂停
HiLog.e(TAG,"whenCreated")
}
whenResumed {
//这里的代码 只有宿主的声明周期为onResume才会执行,否则暂停
HiLog.e(TAG,"whenResumed")
}
whenStarted {
//这里的代码 只有宿主的声明周期为onStrart才会执行,否则暂停
HiLog.e(TAG,"whenStarted")
}
}
//当我们宿主的声明周期至少为onStart的时候才会启动
lifecycleScope.launchWhenStarted {
}
//2.viewModelScope
viewModelScope.launch{}
3.3.启动协成
- launch:可启动新协程而不将结果返回给调用方。
- async:会启动一个新的协程,并可以通过 await 的挂起函数返回结果。
/**
* TODO:GlobalScope.launch参数
* 参数2:CoroutineStart启动模式
* DEFAULT:默认启动模式,创建即启动协程,可随时取消
* LAZY:延时启动模式,只有当我们调用start方法才会启动
* ATOMIC:自动模式,同样创建即启动协程,但启动前不能取消
*/
fun startScene1(){
HiLog.e(TAG,"coroutine is running")
//并不会阻塞当前线程。
val job = GlobalScope.launch(Dispatchers.Main,CoroutineStart.LAZY) {
val request1 = request1()
val request2 = request2(request1)
val request3 = request3(request2)
updateUI(request3)
}
job.start()
HiLog.e(TAG,"coroutine has launched")
}
/**
* TODO:启动一个线程,先执行request1,再同时启动request2,request3,当request2,request3执行完成后更行UI
*/
fun startScene2(){
HiLog.e(TAG,"coroutine is running")
val job = GlobalScope.launch(Dispatchers.Main) {
val request1 = request1()
val deferred2 = GlobalScope.async { request2(request1)}
val deferred3 = GlobalScope.async { request3(request1)}
updateUI2(deferred2.await(),deferred3.await())
}
job.start()
HiLog.e(TAG,"coroutine has launched")
}
3.4.协成的挂起
实例:
object CoroutineScene2{
private val TAG :String = "TAG"
suspend fun request1():String{
val request2 = request2()
HiLog.e(TAG,"request1 completed")
return "result from request1 ----------- $request2"
}
suspend fun request2():String{
delay(5 * 1000)
HiLog.e(TAG,"request2 completed")
return "result2 from requests"
}
}
通过上述的示例代码,我们通过反编译后看一下。
public final class CoroutineScene2 {
private static final String TAG = "TAG";
@NotNull
public static final CoroutineScene2 INSTANCE;
//它是传入一个Continuation接口,
@Nullable
public final Object request1(@NotNull Continuation var1) {
Object $continuation;
//创建一个ContinuationImpl抽象类,将其传入的Continuation接口对象传入,
$continuation = new ContinuationImpl(var1) {
// $FF: synthetic field
Object result;
int label; //默认为0
@Nullable
public final Object invokeSuspend(@NotNull Object $result) {
this.result = $result;
this.label |= Integer.MIN_VALUE;
return CoroutineScene2.this.request1(this);
}
};
}
Object $result = (()$continuation).result;
Object var5 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
Object var10000;
switch((()$continuation).label) {
case 0: //如果label为0,进入,所以第一次都会进入
ResultKt.throwOnFailure($result);
//将其赋值为1
(()$continuation).label = 1;
//调用request2方法,传入的是上面创建的ContinuationImpl
var10000 = this.request2((Continuation)$continuation);
//判断当前状态是否为SUSPENDED,直接return。
if (var10000 == var5) {
return var5;
}
break;
case 1:
ResultKt.throwOnFailure($result);
var10000 = $result;
break;
default:
throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
}
String request2 = (String)var10000;
HiLog.e(new Object[]{TAG, "request1 completed"});
return "result from request1 ----------- " + request2;
}
1.通过反编译后查看其实协程的挂起与恢复核心就是通过return 和 Callback来进行实现。
2.当我们一个函数被suspend关键字修饰,那么它并不一定会被挂起,需要根据挂起函数内的执行代码,如果函数的返回值为COROUTINE_SUSPENDED,那么就会直接return。所以后面代码不会被执行。否则则会通过callback进行回掉,在次调用该方法。
通过Java代码还原协成的挂起与恢复。采用return + Callback的方法。
/**
* TODO:Java代码实现coroutine的挂起函数
* 当我们将协程代码反编译后,它就是通过return结合callback进行实现的。
*
* suspend fun request1(request2:String): String {
* delay(2 * 1000)
* HiLog.e(TAG,"request1 work on ${Thread.currentThread().name}")
* return "result from request1"
* }
*
* suspend fun request2(): String {
* //delay方法并不会暂停线程,但是会暂停当前所在的协程
* delay(2 * 1000)
* HiLog.e("TAG","request2 work on ${Thread.currentThread().name}")
* return "result from request2"
* }
*/
public class CoroutineScene2_decompiled {
public static final Object request1(Continuation preCallback){
/**
* TODO:注册一个回调
*/
ContinuationImpl request1Callback;
//判断一下是否已经包装过,就是为了防止恢复的时候再次包装。
if (!(preCallback instanceof ContinuationImpl) || (((ContinuationImpl) preCallback).label & Integer.MIN_VALUE) == 0){
request1Callback = new ContinuationImpl(preCallback){
@Override
Object invokeSuspend(@NotNull Object resumeResult) {
this.result = resumeResult;
this.label |= Integer.MIN_VALUE;
HiLog.e("TAG","request1 has resume");
//恢复当前挂起函数
return request1(this);
}
};
}else {
request1Callback = (ContinuationImpl) preCallback;
}
/**
* TODO:进行判断状态
*/
switch (request1Callback.label){
case 0: //创建的回调,默认为0.
Object request2 = request2(request1Callback);
if (request2 == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
HiLog.e("TAG","request1 has suspend");
return IntrinsicsKt.getCOROUTINE_SUSPENDED();
}
break;
}
HiLog.e("TAG","request1 work on "+Thread.currentThread().getName());
return "result from request1 ----------- " + request1Callback.result;
}
public static final Object request2(Continuation preCallback){
/**
* TODO:注册一个回调
*/
ContinuationImpl request2Callback;
//判断一下是否已经包装过,就是为了防止恢复的时候再次包装。
if (!(preCallback instanceof ContinuationImpl) || (((ContinuationImpl) preCallback).label & Integer.MIN_VALUE) == 0){
request2Callback = new ContinuationImpl(preCallback){
@Override
Object invokeSuspend(@NotNull Object resumeResult) {
this.result = resumeResult;
this.label |= Integer.MIN_VALUE;
//恢复当前挂起函数
HiLog.e("TAG","request2 has resume");
return request2(this);
}
};
}else {
request2Callback = (ContinuationImpl) preCallback;
}
/**
* TODO:进行判断状态
*/
switch (request2Callback.label){
case 0: //创建的回调,默认为0.
Object delay = DelayKt.delay(2000, request2Callback);
if (delay == IntrinsicsKt.getCOROUTINE_SUSPENDED()) {
HiLog.e("TAG","request2 has suspend");
return IntrinsicsKt.getCOROUTINE_SUSPENDED();
}
break;
}
HiLog.e("TAG","request2 work on "+Thread.currentThread().getName());
return "result from request2";
}
static abstract class ContinuationImpl implements Continuation{
private Continuation preCallback;
Object result;
int label;
public ContinuationImpl(Continuation preCallback) {
this.preCallback = preCallback;
}
@NotNull
@Override
public CoroutineContext getContext() {
return preCallback.getContext();
}
/**
* TODO:该函数就是被挂起函数恢复时所返回的值
* @param resumeResult
*/
@Override
public void resumeWith(@NotNull Object resumeResult) {
Object suspend = invokeSuspend(resumeResult);
//1.再次判断当前值是否还等于SUSPENDED,如果还等于表示你虽然恢复了,但是又被挂起了
if (suspend == IntrinsicsKt.getCOROUTINE_SUSPENDED()){
return;
}
//2.此时就是真正的恢复了,调用我们包装传入的Continuation回调的resumeResult。
preCallback.resumeWith(suspend);
}
abstract Object invokeSuspend(@NotNull Object resumeResult);
}
}
4.协成分析
//CoroutineScope.launch不会阻塞当前线程
public fun CoroutineScope.launch(
//1.协程上下文,也就是协程调度器,如果不指定,默认为Default,也就是IO
Dispatchers.IO:子线程
Dispatchers.Main:主线程
Dispatchers.Unconfined
context: CoroutineContext = EmptyCoroutineContext,
//2.协程启动模式:
CoroutineStart.DEFAULT:默认会创建即启动协程,可随时取消
CoroutineStart.LAZY :延时启动模式,当我们手动调用start()方法实例启动协程
CoroutineStart.ATOMIC :自动模式,同样创建即启动协程,但启动前不能取消
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job {
//如果不指定调度器,默认会为我们创建一个Default,也就是IO
val newContext = newCoroutineContext(context)
val coroutine = if (start.isLazy)
LazyStandaloneCoroutine(newContext, block) else
StandaloneCoroutine(newContext, active = true)
//启动协程
coroutine.start(start, coroutine, block)
return coroutine
}