我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Kotlin协程launch启动流程原理详解

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Kotlin协程launch启动流程原理详解

1.launch启动流程

已知协程的启动方式之一是Globalscope.launch,那么Globalscope.launch的流程是怎样的呢,直接进入launch的源码开始看起。

fun main() {
    coroutineTest()
    Thread.sleep(2000L)
}
val block = suspend {
    println("Hello")
    delay(1000L)
    println("Kotlin")
}
private fun coroutineTest() {
    CoroutineScope(Job()).launch {
        withContext(Dispatchers.IO) {
            block.invoke()
        }
    }
}

反编译后的Java代码

public final class CoroutineDemoKt {
   @NotNull
   private static final Function1 block;
   public static final void main() {
      coroutineTest();
      Thread.sleep(2000L);
   }
   // $FF: synthetic method
   public static void main(String[] var0) {
      main();
   }
   @NotNull
   public static final Function1 getBlock() {
      return block;
   }
   private static final void coroutineTest() {
      BuildersKt.launch$default(CoroutineScopeKt.CoroutineScope((CoroutineContext)JobKt.Job$default((Job)null, 1, (Object)null)), (CoroutineContext)null, (CoroutineStart)null, (Function2)(new Function2((Continuation)null) {
         int label;
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               CoroutineContext var10000 = (CoroutineContext)Dispatchers.getIO();
               Function2 var10001 = (Function2)(new Function2((Continuation)null) {
                  int label;
                  @Nullable
                  public final Object invokeSuspend(@NotNull Object $result) {
                     Object var2 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
                     switch(this.label) {
                     case 0:
                        ResultKt.throwOnFailure($result);
                        Function1 var10000 = CoroutineDemoKt.getBlock();
                        this.label = 1;
                        if (var10000.invoke(this) == var2) {
                           return var2;
                        }
                        break;
                     case 1:
                        ResultKt.throwOnFailure($result);
                        break;
                     default:
                        throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
                     }
                     return Unit.INSTANCE;
                  }
                  @NotNull
                  public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
                     Intrinsics.checkNotNullParameter(completion, "completion");
                     Function2 var3 = new <anonymous constructor>(completion);
                     return var3;
                  }
                  public final Object invoke(Object var1, Object var2) {
                     return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
                  }
               });
               this.label = 1;
               if (BuildersKt.withContext(var10000, var10001, this) == var2) {
                  return var2;
               }
               break;
            case 1:
               ResultKt.throwOnFailure($result);
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }
            return Unit.INSTANCE;
         }
         @NotNull
         public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function2 var3 = new <anonymous constructor>(completion);
            return var3;
         }
         public final Object invoke(Object var1, Object var2) {
            return ((<undefinedtype>)this.create(var1, (Continuation)var2)).invokeSuspend(Unit.INSTANCE);
         }
      }), 3, (Object)null);
   }
   static {
      Function1 var0 = (Function1)(new Function1((Continuation)null) {
         int label;
         @Nullable
         public final Object invokeSuspend(@NotNull Object $result) {
            Object var3 = IntrinsicsKt.getCOROUTINE_SUSPENDED();
            String var2;
            switch(this.label) {
            case 0:
               ResultKt.throwOnFailure($result);
               var2 = "Hello";
               System.out.println(var2);
               this.label = 1;
               if (DelayKt.delay(1000L, this) == var3) {
                  return var3;
               }
               break;
            case 1:
               ResultKt.throwOnFailure($result);
               break;
            default:
               throw new IllegalStateException("call to 'resume' before 'invoke' with coroutine");
            }
            var2 = "Kotlin";
            System.out.println(var2);
            return Unit.INSTANCE;
         }
         @NotNull
         public final Continuation create(@NotNull Continuation completion) {
            Intrinsics.checkNotNullParameter(completion, "completion");
            Function1 var2 = new <anonymous constructor>(completion);
            return var2;
         }
         public final Object invoke(Object var1) {
            return ((<undefinedtype>)this.create((Continuation)var1)).invokeSuspend(Unit.INSTANCE);
         }
      });
      block = var0;
   }
}

先分析一下上面代码的流程:

  • 首先声明了一个Function1类型的block变量,这个变量就是demo中的block,然后会在static函数中会被赋值。
  • 接下来就是coroutineTest函数的调用。这个函数中的第一行代码就是CoroutineScope的传参和一些默认值
  • 然后通过89行的invoke进入到了外层状态机流转的过程
  • 95行的static表示的是内部的挂起函数就是demo中的block.invoke,它是以匿名内部类的方式实现,然后执行内部的状态机流转过程,最后给block赋值。
  • block被赋值后最终在Function1 var10000 = CoroutineDemoKt.getBlock();被调用

那么这个过程又是如何实现的,进入launch源码进行查看:

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
}

这里的block指的就是demo中的block代码段

再来看一下里面的几行代码的含义:

  • newCoroutineContext: 通过默认的或者传入的context创建一个新的Context;
  • coroutine: launch 会根据传入的启动模式来创建对应的协程对象。这里有两种,一种是标准的,一种是懒加载的。
  • coroutine.start: 尝试启动协程

2.协程是如何被启动的

通过launch的源码可知协程的启动是通过coroutine.start启动的,那么协程的启动流程又是怎样的?

public abstract class AbstractCoroutine<in T>(
    parentContext: CoroutineContext,
    initParentJob: Boolean,
    active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope {
    ...
    
    public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
        start(block, receiver, this)
    }
}

start函数中传入了三个参数,只需要关注第一个参数即可。

public enum class CoroutineStart {
    ...
    
    public operator fun <T> invoke(block: suspend () -> T, completion: Continuation<T>): Unit =
    when (this) {
        DEFAULT -> block.startCoroutineCancellable(completion)
        ATOMIC -> block.startCoroutine(completion)
        UNDISPATCHED -> block.startCoroutineUndispatched(completion)
        LAZY -> Unit // will start lazily
    }
}

启动策略的具体实现有三种方式,这里只需要分析startCoroutine,另外两个其实就是它的基础上增加了一些功能,其中前者代表启动协程以后可以在等待调度时取消,后者表示协程启动后不会被分发。

 
public fun <T> (suspend () -> T).startCoroutine(
    completion: Continuation<T>
) {
    createCoroutineUnintercepted(completion).intercepted().resume(Unit)
}

createCoroutineUnintercepted在源代码中只是一个声明,它的具体实现是在IntrinsicsJvm.kt文件中。

//IntrinsicsJvm.kt#createCoroutineUnintercepted

public actual fun <T> (suspend () -> T).createCoroutineUnintercepted(
    completion: Continuation<T>
): Continuation<Unit> {
    val probeCompletion = probeCoroutineCreated(completion)
    return if (this is BaseContinuationImpl)
        create(probeCompletion)
    else
        createCoroutineFromSuspendFunction(probeCompletion) {
            (this as Function1<Continuation<T>, Any?>).invoke(it)
        }
}

actual代表了 createCoroutineUnintercepted() 在 JVM 平台的实现。

createCoroutineUnintercepted是一个扩展函数,接收者类型是一个无参数,返回值为 T 的挂起函数或者 Lambda。

第9行代码中的this代表的是(suspend () -> T)也就是invoke函数中的block变量,这个block变量就是demo中的block代码段。

第9行的BaseContinuationImpl是一个抽象类它实现了Continuation

关于if (this is BaseContinuationImpl)的结果暂且不分析,先分析两种情况下的create函数:

  • create(probeCompletion):
//ContinuationImpl.kt#create
public open fun create(completion: Continuation<*>): Continuation<Unit> {
    throw UnsupportedOperationException("create(Continuation) has not been overridden")
}
public open fun create(value: Any?, completion: Continuation<*>): Continuation<Unit> {
    throw UnsupportedOperationException("create(Any?;Continuation) has not been overridden")
}

这个create函数抛出一个异常,意思就是这个create()没有被重写,而这个create()的重写就是在反编译后的Java代码中的create函数

@NotNull
public final Continuation create(@Nullable Object value, @NotNull Continuation completion) {
    Intrinsics.checkNotNullParameter(completion, "completion");
    Function2 var3 = new <anonymous constructor>(completion);
    return var3;
}
  • createCoroutineFromSuspendFunction(probeCompletion):
//IntrinsicsJvm.kt#createCoroutineFromSuspendFunction

private inline fun <T> createCoroutineFromSuspendFunction(
	completion: Continuation<T>,
	crossinline block: (Continuation<T>) -> Any?
		): Continuation<Unit> {
	val context = completion.context
	// context为空创建一个受限协程
	return if (context === EmptyCoroutineContext)
	//受限协程:只能调用协程作用域中提供的挂起方式挂起,其他挂起方法不能调用
	object : RestrictedContinuationImpl(completion as Continuation<Any?>) {
		private var label = 0
		override fun invokeSuspend(result: Result<Any?>): Any? =
		when (label) {
			0 -> {
				label = 1
				result.getOrThrow() // 如果试图以异常开始,则重新抛出异常(将被BaseContinuationImpl.resumeWith捕获)
				block(this) // 运行块,可以返回或挂起
			}
			1 -> {
				label = 2
				result.getOrThrow() // 这是block挂起的结果
			}
			else -> error("This coroutine had already completed")
		}
	}
	else
	//创建一个正常的协程
	object : ContinuationImpl(completion as Continuation<Any?>, context) {
		private var label = 0
		override fun invokeSuspend(result: Result<Any?>): Any? =
		when (label) {
			0 -> {
				label = 1
				result.getOrThrow() // 如果试图以异常开始,则重新抛出异常(将被BaseContinuationImpl.resumeWith捕获)
				block(this) // 运行块,可以返回或挂起
			}
			1 -> {
				label = 2
				result.getOrThrow() // 这是block挂起的结果
			}
			else -> error("This coroutine had already completed")
		}
	}
}

createCoroutineFromSuspendFunction就是当一个被suspend修饰的Lambda表达式没有继承BaseContinuationImpl是才会被调用,然后根据上下文是否为空创建不同类型的协程。

两种情况都已经分析完了,那么现在if (this is BaseContinuationImpl)会执行哪一个呢,首先这里的this所指的就是demo中的block代码段,Kotlin编译器编译后会自动生成一个类就是上面的static,它会继承SuspendLambda类,而这个SuspendLambda类继承自ContinuationImpl,ContinuationImpl继承自BaseContinuationImpl,因此可以得到判断结果为true,

createCoroutineUnintercepted的过程就是协程创建的过程。

然后就是intercepted函数,这个函数的具体实现也在IntrinsicsJvm.kt中,那么intercepted又做了什么呢

public expect fun <T> Continuation<T>.intercepted(): Continuation<T>
//具体实现
//IntrinsicsJvm.kt#intercepted
public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
    (this as? ContinuationImpl)?.intercepted() ?: this

首先有个强转,通过上面的分析这个强转是一定会成功的,到这里intercepted就进入到了ContinuationImpl中了

internal abstract class ContinuationImpl(
    completion: Continuation<Any?>?,
    private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
	...
    @Transient
    private var intercepted: Continuation<Any?>? = null
	//如果没有缓存,则从上下文获取拦截器,调用interceptContinuation进行拦截
	//将获取到的内容保存到全局变量
    public fun intercepted(): Continuation<Any?> =
        intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }
}

这里的ContinuationInterceptor指的就是Demo中传输的Dispatcher.IO,默认值时Dispatcher.Default

再回到startContinue中还剩最后一个resume


public inline fun <T> Continuation<T>.resume(value: T): Unit =
	resumeWith(Result.success(value))
public interface Continuation<in T> {
	
	public val context: CoroutineContext
	
	public fun resumeWith(result: Result<T>)
}

这里的resume(Unit)作用就相当与启动了一个协程。

上面的启动流程中为了方便分析的是CoroutineStart.ATOMIC,而默认的是CoroutineStart.DEFAULT,下面分析一下DEFAULT的流程

//Cancellable.kt#startCoroutineCancellable
public fun <T> (suspend () -> T).startCoroutineCancellable(completion: Continuation<T>): Unit = runSafely(completion) {
    createCoroutineUnintercepted(completion).intercepted().resumeCancellableWith(Result.success(Unit))
}

startCoroutineCancellable对于协程的创建和拦截与ATOMIC是一样的,区别就在于resumeCancellableWith

//DispatchedContinuation#resumeCancellableWith
public fun <T> Continuation<T>.resumeCancellableWith(
	result: Result<T>,
	onCancellation: ((cause: Throwable) -> Unit)? = null
): Unit = when (this) {
	is DispatchedContinuation -> resumeCancellableWith(result, onCancellation)
	else -> resumeWith(result)
}
// 我们内联它来保存堆栈上的一个条目,在它显示的情况下(无限制调度程序)
// 它只在Continuation<T>.resumeCancellableWith中使用
@Suppress("NOTHING_TO_INLINE")
inline fun resumeCancellableWith(
	result: Result<T>,
	noinline onCancellation: ((cause: Throwable) -> Unit)?
		) {
	val state = result.toState(onCancellation)
	//是否需要分发
	if (dispatcher.isDispatchNeeded(context)) {
		_state = state
		resumeMode = MODE_CANCELLABLE
		//将可运行块的执行分派给给定上下文中的另一个线程
		dispatcher.dispatch(context, this)
	} else {
		executeUnconfined(state, MODE_CANCELLABLE) {
			//协程未被取消
			if (!resumeCancelled(state)) {
				// 恢复执行
				resumeUndispatchedWith(result)
			}
		}
	}
}
//恢复执行前判断协程是否已经取消执行
inline fun resumeCancelled(state: Any?): Boolean {
	//获取当前协程任务
	val job = context[Job]
	//如果不为空且不活跃
	if (job != null && !job.isActive) {
		val cause = job.getCancellationException()
		cancelCompletedResult(state, cause)
		//抛出异常
		resumeWithException(cause)
		return true
	}
	return false
}
//我们需要内联它来在堆栈中保存一个条目
inline fun resumeUndispatchedWith(result: Result<T>) {
	withContinuationContext(continuation, countOrElement) {
		continuation.resumeWith(result)
	}
}

以上就是Kotlin协程launch启动流程原理详解的详细内容,更多关于Kotlin协程launch启动流程的资料请关注编程网其它相关文章!

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Kotlin协程launch启动流程原理详解

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

Kotlin协程launch启动流程原理详解

这篇文章主要为大家介绍了Kotlin协程launch启动流程原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-08

Kotlin协程launch原理详解

这篇文章主要为大家介绍了Kotlin协程launch原理的示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Kotlin协程启动createCoroutine及创建startCoroutine原理

这篇文章主要为大家介绍了Kotlin协程启动createCoroutine及创建startCoroutine原理详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Kotlin协程Dispatchers原理示例详解

这篇文章主要为大家介绍了Kotlin协程Dispatchers原理示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-11-13

Kotlin启动协程的三种方式示例详解

这篇文章主要为大家介绍了Kotlin启动协程的三种方式示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2022-12-08

Kotlin协程操作之创建启动挂起恢复详解

本文的定位是协程的创建、启动、挂起、恢复,也会示例一些简单的使用,这里不对suspend讲解,,也不对协程的高级用法做阐述(热数据通道Channel、冷数据流Flow...),本文主要讲协程稍微深入的全面知识
2022-11-13

Kotlin使用协程实现高效并发程序流程详解

这篇文章主要介绍了Kotlin使用协程实现高效并发程序流程,协程属于Kotlin中非常有特色的一项技术,因为大部分编程语言中是没有协程这个概念的。那么什么是协程呢?它其实和线程有点相似,可以简单地将它理解成一种轻量级的线程
2023-01-18

AndroidActivityManagerService启动流程详解

这篇文章主要介绍了AndroidActivityManagerService启动流程,AMS,即ActivityManagerService,是安卓javaframework的一个服务,运行在system_server进程。此服务十分重要,因为它管理着安卓的四大组件,是安卓APP开发者最常接触到的一个服务
2023-02-17

SpringBoot之启动流程详解

SpringBoot是一个基于Spring框架的快速开发框架,旨在简化Spring应用程序的开发和部署。在本文中,我们将深入分析SpringBoot启动过程的源代码,并提供必要的解释和说明
2023-05-17

Android Service启动绑定流程详解

这篇文章主要为大家介绍了Android Service启动绑定流程详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
2023-03-08

详解Android广播Broadcast的启动流程

Android中的广播是一种用于应用程序之间通信的机制。它允许应用程序发送和接收系统级或应用程序级的广播消息。当一个广播被发送时,系统会自动启动广播接收器来处理该广播。下面是Android广播的启动流程:1. 广播发送:应用程序发送一个广播
2023-08-11

SpringBoot热部署启动关闭流程详解

Spring Boot启动热部署是一种技术,它能让开发者在不重启应用程序的情况下实时更新代码。这样可以提高开发效率,避免频繁重启应用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习吧
2023-05-17

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录