public final override suspend fun join() {
// 调用joinInternal方法
if (!joinInternal()) {
// 对当前协程的上下文进行检查,检查是否是完成状态
coroutineContext.checkCompletion()
// 直接返回,不挂起
return
}
// 挂起
return joinSuspend()
}
private fun joinInternal(): Boolean {
// 循环
loopOnState { state ->
// 如果当前是FINAL_*状态,返回false
if (state !is Incomplete) return false
// 启动协程,返回结果不为RETRY,则返回true
if (startInternal(state) >= 0) return true
}
}
// 检查上下文
internal fun CoroutineContext.checkCompletion() {
// 获取上下文的Job对象
val job = get(Job)
// 如果job不为空,同时还未启动,则抛出异常
if (job != null && !job.isActive) throw job.getCancellationException()
}
join方法中最终通过joinSuspend方法,实现挂起执行,代码如下:
private suspend fun joinSuspend() = suspendCancellableCoroutine { cont ->
cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion(this, cont).asHandler))
}
joinSuspend方法内部调用了suspendCancellableCoroutine方法。
[]( )1.suspendCancellableCoroutine方法
-------------------------------------------------------------------------------------------------
这个方法用于挂起当前的协程,同时返回一个可取消的续体。代码如下:
public suspend inline fun suspendCancellableCoroutine(
crossinline block: (CancellableContinuation<T>) -> Unit
): T =
suspendCoroutineUninterceptedOrReturn { uCont ->// 核心方法,返回一个续体
// 将返回的续体包装成可取消的续体,这里进行了拦截
val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
// 将cancellable绑定到uCont.intercepted()返回的续体上,
// 这样当外部取消内部会得到通知
cancellable.initCancellability()
// 执行任务
block(cancellable)
// 获取结果
cancellable.getResult()
}
### []( )1)suspendCoroutineUninterceptedOrReturn方法
调用该方法会获取当前挂起函数中的续体,并作为参数,去调用block执行。如果需要挂起,则需要在block中返回COROUTINE\_SUSPENDED,并且在合适的时间去手动的调用续体的resumeWith方法,来恢复执行。如果不需要挂起,则不可以对续体进行任何操作。
该方法暴露的续体未经过拦截器拦截,续体将在被调用的线程上去执行。可以手动调用intercepted方法,对续体进行拦截。
该方法是协程的内部实现,代码不可见。方法声明如下:
@SinceKotlin(“1.3”)
@InlineOnly
@Suppress(“UNUSED_PARAMETER”, “RedundantSuspendModifier”)
public suspend inline fun suspendCoroutineUninterceptedOrReturn(crossinline block: (Continuation) -> Any?): T {
// 通知虚拟机,block只能调用一次
contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) }
throw NotImplementedError("Implementation of suspendCoroutineUninterceptedOrReturn is intrinsic")
}
**suspendCoroutineUninterceptedOrReturn方法图解:**
![在这里插入图片描述](https://img-blog.csdnimg.cn/5c48bce7c97b46529dc8d062eca7e123.png#pic_center)
### []( )2)CancellableContinuationImpl类
CancellableContinuationImpl类与JobSupport类类似,内部也是通过状态机实现的。不同之处在于,它只能有取消监听器,而且最多只能有一个。一旦发生取消,会立即回调取消监听器。它的取消监听器在设置后不能取消。
CancellableContinuationImpl类中有两个状态机,一个是决策状态机,用于控制挂起和恢复,状态保存在全局变量\_decision中,代码如下:
private const val UNDECIDED = 0 // 初始状态
private const val SUSPENDED = 1 // 挂起状态
private const val RESUMED = 2 // 恢复状态
private val _decision = atomic(UNDECIDED)
对应的状态转移图如下:
![在这里插入图片描述](https://img-blog.csdnimg.cn/cbbed2ff30a74ae285b8e7c11d1b1228.png#pic_center)
trySuspend与tryResume方法代码如下:
private fun trySuspend(): Boolean {
// 循环
_decision.loop { decision ->
when (decision) {
// 进入挂起状态
UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, SUSPENDED)) return true
RESUMED -> return false
else -> error("Already suspended")
}
}
}
private fun tryResume(): Boolean {
// 循环
_decision.loop { decision ->
when (decision) {
// 进入恢复状态
UNDECIDED -> if (this._decision.compareAndSet(UNDECIDED, RESUMED)) return true
SUSPENDED -> return false
else -> error("Already resumed")
}
}
}
另一个状态机则类似于JobSupport类中的状态机器,状态保存在全局变量\_state中,代码如下:
private val _state = atomic<Any?>(Active)
状态的划分与对应的外部状态如下:
| 内部状态名 | 内部实现类或对象 | 外部状态 | 备注 |
| --- | --- | --- | --- |
| ACTIVE | Active | Active | 没有监听器 |
| SINGLE\_A | CancelHandler | Active | 有一个取消监听器 |
| CANCELLED | CancelledContinuation | Cancelled | 取消的最终状态 |
| COMPLETED | <Any> | Completed | 完成状态 |
与JobSupport类相比,CancellableContinuationImpl类的状态类更多的是作为标记来表示一个状态,代码如下:
internal interface NotCompleted
// 实现了NotCompleted接口
private object Active : NotCompleted {
override fun toString(): String = "Active"
}
// 实现了NotCompleted接口
internal abstract class CancelHandler : CancelHandlerBase(), NotCompleted
// 继承自CompletedExceptionally
internal class CancelledContinuation(
continuation: Continuation<*>,
cause: Throwable?,
handled: Boolean
) : CompletedExceptionally(cause ?: CancellationException(“Continuation $continuation was cancelled normally”), handled) {
private val _resumed = atomic(false)
fun makeResumed(): Boolean = _resumed.compareAndSet(false, true)
}
### []( )3)getResult方法
在suspendCancellableCoroutine方法中,最后调用了CancellableContinuationImpl类的getResult方法,代码如下:
@PublishedApi
internal fun getResult(): Any? {
// 与initCancellability方法相同,监听取消回调
setupCancellation()
// 尝试挂起,成功则返回COROUTINE_SUSPENDED挂起
if (trySuspend()) return COROUTINE_SUSPENDED
// 获取状态
val state = this.state
if (state is CompletedExceptionally) throw recoverStackTrace(state.cause, this)
// 如果当前的模式是取消模式
if (resumeMode.isCancellableMode) {
// 获取父协程
val job = context[Job]
// 如果父协程被取消
if (job != null && !job.isActive) {
// 获取异常
val cause = job.getCancellationException()
// 通知回调取消监听器,并修改对应状态
cancelCompletedResult(state, cause)
// 抛出异常
throw recoverStackTrace(cause, this)
}
}
// 获取结果
return getSuccessfulResult(state)
}
到这里join方法实现了协程的挂起。
接下来分析,如果在挂起过程中协程发生了取消,将如何恢复续体的执行?
[]( )2.disposeOnCancellation方法
-------------------------------------------------------------------------------------------
private suspend fun joinSuspend() = suspendCancellableCoroutine { cont ->
cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion(this, cont).asHandler))
}
返回到joinSuspend方法中,在获取到续体cont后,首先将续体和当前的Job对象,封装成了ResumeOnCompletion类的对象,接着调用了asHandler方法,转换成了(cause: Throwable?) -> Unit类型的lambda表达式,然后调用了invokeOnCompletion方法,注册协程执行完成时的回调,同时获取了一个DisposableHandle类型的对象,最后,作为参数传入到disposeOnCancellation方法中,代码如下:
@InternalCoroutinesApi
public fun CancellableContinuation<*>.disposeOnCancellation(handle: DisposableHandle): Unit =
invokeOnCancellation(handler = DisposeOnCancel(handle).asHandler)
该方法是一个扩展方法。该方法将传入的参数,进一步封装成了DisposeOnCancel对象,最终作为参数,通过调用CancellableContinuation类的invokeOnCancellation方法,注册监听取消。
假设在挂起到过程中,协程取消会调用DisposeOnCancel的invoke方法,代码如下:
private class DisposeOnCancel(private val handle: DisposableHandle) : CancelHandler() {
override fun invoke(cause: Throwable?) = handle.dispose()
override fun toString(): String = "DisposeOnCancel[$handle]"
}
DisposeOnCancel类的对象内部会继续调用DisposableHandle对象的dispose方法,这个DisposableHandle是在调用invokeOnCompletion时候返回的,这个对象的实际类型为InvokeOnCompletion,代码如下:
private class InvokeOnCompletion(
job: Job,
private val handler: CompletionHandler
) : JobNode(job) {
override fun invoke(cause: Throwable?) = handler.invoke(cause)
override fun toString() = "InvokeOnCompletion[$classSimpleName@$hexAddress]"
}
public typealias CompletionHandler = (cause: Throwable?) -> Unit
InvokeOnCompletion类的对象内部又回继续调用CompletionHandler类对象的invoke方法,而这个CompletionHandler对象就是之前传入的ResumeOnCompletion类对象,代码如下:
private class ResumeOnCompletion(
job: Job,
private val continuation: Continuation<Unit>
) : JobNode(job) {
override fun invoke(cause: Throwable?) = continuation.resume(Unit)
override fun toString() = "ResumeOnCompletion[$continuation]"
}
ResumeOnCompletion类对象的invoke方法,最终调用了续体的reusme方法,恢复了执行。
[]( )四.cancel方法
============================================================================
cancel方法用于取消一个线程的执行,该方法在JobSupport类中实现,代码如下:
public override fun cancel(cause: CancellationException?) {
cancelInternal(cause ?: defaultCancellationException())
}
@Suppress(“NOTHING_TO_INLINE”)
internal inline fun defaultCancellationException(message: String? = null, cause: Throwable? = null) =
JobCancellationException(message ?: cancellationExceptionMessage(), cause, this)
cancel方法内部调用了cancelInternal方法,并传入取消异常,代码如下:
public open fun cancelInternal(cause: Throwable) {
cancelImpl(cause)
}
cancelInternal方法内部调用了cancelImpl方法,代码如下:
// 如果异常已经被处理,则返回true,否则返回false
internal fun cancelImpl(cause: Any?): Boolean {
// 最终状态,默认已经完成
var finalState: Any? = COMPLETING_ALREADY
// 如果协程进入Completing状态
if (onCancelComplete) {
// 通知子协程进入完成状态
finalState = cancelMakeCompleting(cause)
// 如果返回状态为等待子协程完成,则返回true
if (finalState === COMPLETING_WAITING_CHILDREN) return true
}
// 如果状态为已经完成
if (finalState === COMPLETING_ALREADY) {
// 则进入Cancelling状态
finalState = makeCancelling(cause)
}
// 返回
return when {
finalState === COMPLETING_ALREADY -> true
finalState === COMPLETING_WAITING_CHILDREN -> true
finalState === TOO_LATE_TO_CANCEL -> false
else -> {
// 其他状态,通过回调通知
afterCompletion(finalState)
true
}
}
}
// 结束的四种状态
// 已经处于完成状态
@SharedImmutable
private val COMPLETING_ALREADY = Symbol(“COMPLETING_ALREADY”)
// 等待子协程进入完成状态
@JvmField
@SharedImmutable
internal val COMPLETING_WAITING_CHILDREN = Symbol(“COMPLETING_WAITING_CHILDREN”)
// 再次重试
@SharedImmutable
private val COMPLETING_RETRY = Symbol(“COMPLETING_RETRY”)
// 由于Finishing类已经密封,因此无法取消
@SharedImmutable
private val TOO_LATE_TO_CANCEL = Symbol(“TOO_LATE_TO_CANCEL”)
// 该变量返回true,表示当前的协程没有block任务待完成,
// 应该马上进入Completing状态,等待子协程完成
internal open val onCancelComplete: Boolean get() = false
接下来,分析makeCancelling方法如何取消子协程,代码如下:
private fun makeCancelling(cause: Any?): Any? {
// 局部的异常缓存
var causeExceptionCache: Throwable? = null
// 循环
loopOnState { state ->
when (state) {
// 如果是Completing状态或者Cancelling状态
is Finishing -> {
// 获取最根本的异常
val notifyRootCause = synchronized(state) {
// 如果已经密封,则返回TOO_LATE_TO_CANCEL
if (state.isSealed) return TOO_LATE_TO_CANCEL
// 是否已经处于Cancelling状态
val wasCancelling = state.isCancelling
// 如果传入的异常不为空,或者还未处于Cancelling状态
if (cause != null || !wasCancelling) {
// 则对异常进行包装,并记录到状态中
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
state.addExceptionLocked(causeException)
}
// 返回根本异常,如果wasCancelling为false,上面addExceptionLocked
// 会把causeException保存到rootCause
state.rootCause.takeIf { !wasCancelling }
}
// 如果根本异常不为空,说明已经进入Cancelling状态,则通知子协程取消
notifyRootCause?.let { notifyCancelling(state.list, it) }
// 返回COMPLETING_ALREADY
return COMPLETING_ALREADY
}
// 如果还没有进入Completing状态或者Cancelling状态
is Incomplete -> {
// 获取异常
val causeException = causeExceptionCache ?: createCauseException(cause).also { causeExceptionCache = it }
// 如果处于启动的状态
if (state.isActive) {
// 调用tryMakeCancelling方法,尝试进入Cancelling状态,
// 成功则返回COMPLETING_ALREADY
if (tryMakeCancelling(state, causeException)) return COMPLETING_ALREADY
} else {// 如果还未启动
// 则尝试进入Completing状态,
// 封装成CompletedExceptionally对象
val finalState = tryMakeCompleting(state, CompletedExceptionally(causeException))
// 根据返回状态处理
when {
finalState === COMPLETING_ALREADY -> error("Cannot happen in $state")
finalState === COMPLETING_RETRY -> return@loopOnState // 继续循环
else -> return finalState
}
}
}
// 其他状态
else -> return TOO_LATE_TO_CANCEL
}
}
}
makeCancelling方法调用notifyCancelling方法取消子协程的执行,代码如下:
private fun notifyCancelling(list: NodeList, cause: Throwable) {
onCancelling(cause)
notifyHandlers<JobCancellingNode<*>>(list, cause)
cancelParent(cause)
}
notifyCancelling中做了三件事,首先是回调onCancelling方法,代码如下:
protected open fun onCancelling(cause: Throwable?) {}
当协程完成或者取消时,就会调用一次这个方法。该方法与调用invokeOnCompletion方法并把参数onCancelling设置为true相似。如果cause为空,说明协程是正常执行结束。如果cause为CancellationException类型的对象,说明协程是正常取消,如果是其他的异常,说明是执行过程中发生错误导致。
接着,在notifyCancelling方法中调用notifyHandlers方法,取消监听器和子协程,代码如下:
private inline fun <reified T: JobNode<*>> notifyHandlers(list: NodeList, cause: Throwable?) {
var exception: Throwable? = null
// 遍历调用invoke方法
list.forEach<T> { node ->
try {
node.invoke(cause)
} catch (ex: Throwable) {
exception?.apply { addSuppressedThrowable(ex) } ?: run {
exception = CompletionHandlerException("Exception in completion handler $node for $this", ex)
}
}
}
// 如果取消的过程中发生异常,则调用handleOnCompletionException方法处理
exception?.let { handleOnCompletionException(it) }
}
如果node是监听器,那它就是(cause: Throwable?) -> Unit类型的lambda表达式,则这时调用invoke方法会直接回调。
如果node是子协程,那就是一个ChildHandleNode类型的对象,ChildHandleNode对象是在父子协程绑定时返回的对象,后面会提到,它的代码如下:
internal class ChildHandleNode(
parent: JobSupport,
@JvmField val childJob: ChildJob
) : JobCancellingNode(parent), ChildHandle {
override fun invoke(cause: Throwable?) = childJob.parentCancelled(job)
override fun childCancelled(cause: Throwable): Boolean = job.childCancelled(cause)
override fun toString(): String = "ChildHandle[$childJob]"
}
调用invoke方法,回调用子协程的parentCancelled方法,该方法代码如下:
public final override fun parentCancelled(parentJob: ParentJob) {
cancelImpl(parentJob)
}
parentCancelled方法也是通过cancelImpl方法实现的。
最后,在notifyCancelling方法中调用了cancelParent方法取消父协程,代码如下:
// 该方法返回true,表示父协程将处理异常,返回false,则表示不处理
private fun cancelParent(cause: Throwable): Boolean {
// 如果当前的协程是作用域协程,则直接返回true
if (isScopedCoroutine) return true
// 判断当前的异常是否是正常的取消
// 如果子协程是正常的取消,父协程不需要取消自身,除非子协程抛出的是其他的异常
val isCancellation = cause is CancellationException
// 获取父协程的handler
val parent = parentHandle
// 如果当前协程没有父协程,说明他是顶级协程
if (parent === null || parent === NonDisposableHandle) {
// 直接返回
return isCancellation
}
// 如果不是顶级协程,则调用childCancelled方法取消自身
return parent.childCancelled(cause) || isCancellation
}
// 作用域协程是在一个封闭作用域内顺序的执行任务,没有任何的并发
// 作用域协程会将遇到的任何错误都抛出
// coroutineScope、withTimeout、runBlocking、ScopeCoroutine都是作用域协程
protected open val isScopedCoroutine: Boolean get() = false
parent是一个ChildHandle接口指向的对象,该接口最终被上面提到的ChildHandleNode类实现。childCancelled方法最终又会调用JobSupport类的childCancelled方法,代码如下:
// 该方法返回值表示父协程是否取消自身
public open fun childCancelled(cause: Throwable): Boolean {
// 如果是正常取消,则直接返回true
if (cause is CancellationException) return true
// 取消自身
return cancelImpl(cause) && handlesException
}
childCancelled方法也是通过cancelImpl方法实现的。
在[Kotlin协程:协程的基础与使用]( )中提到的supervisorScope方法不受子协程取消影响,就是重写了这个方法,代码如下:
public suspend fun supervisorScope(block: suspend CoroutineScope.() -> R): R {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
// 获取续体
return suspendCoroutineUninterceptedOrReturn { uCont ->
// 将续体封装成SupervisorCoroutine协程
val coroutine = SupervisorCoroutine(uCont.context, uCont)
// 启动协程执行block
coroutine.startUndispatchedOrReturn(coroutine, block)
}
}
private class SupervisorCoroutine(
context: CoroutineContext,
uCont: Continuation<T>
) : ScopeCoroutine(context, uCont) {
// 永远返回false
override fun childCancelled(cause: Throwable): Boolean = false
}
[]( )五.delay方法
===========================================================================
delay方法用于暂停协程的执行,等到指定时间后再恢复协程的执行。delay方法不会挂起线程,同时delay方法导致的挂起是可以被取消中断的。delay方法没有定义在Job接口中,是一个独立的方法,代码如下:
public suspend fun delay(timeMillis: Long) {
// 时间小于0,没有意义,直接返回
if (timeMillis <= 0) return
// 直接挂起,返回可取消的续体
return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
// 如果时间超过Long.MAX_VALUE,相当于一直等待,可以调用awaitCancellation方法
if (timeMillis < Long.MAX_VALUE) {
// 核心实现
cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
}
}
}
在上面的代码中,如果时间满足要求,会调用suspendCancellableCoroutine方法挂起,挂起后会获取续体cont的上下文context,并从上下文context中获取一个delay对象,代码如下:
internal val CoroutineContext.delay: Delay get() =
get(ContinuationInterceptor) as? Delay ?: DefaultDelay
delay对象是一个扩展属性,会从上下文中获取拦截器,并强制转换成Delay接口指向的对象。如果转换失败,则使用默认的DefaultDelay对象。
接下来会调用Delay接口中定义的scheduleResumeAfterDelay方法,在等待指定时间后,恢复协程的执行。
[]( )1.DefaultDelay
--------------------------------------------------------------------------------
如果使用的是默认的DefaultDelay对象,则对应的调度器是DefaultExecutor,代码如下:
internal actual val DefaultDelay: Delay = DefaultExecutor
DefaultExecutor类继承了EventLoopImplBase类,EventLoopImplBase类就是在[Kotlin协程:续体、续体拦截器、调度器]( )中提到的实现了Delay接口的EventLoop。代码如下:
private const val MAX_DELAY_NS = Long.MAX_VALUE / 2
public override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) {
// 转换成纳秒,保证执行精度
val timeNanos = delayToNanos(timeMillis)
// 时间范围判断
if (timeNanos < MAX_DELAY_NS) {
// 获取当前时间
val now = nanoTime()
// 封装成DelayedResumeTask
DelayedResumeTask(now + timeNanos, continuation).also { task ->
// 注册续体取消监听
continuation.disposeOnCancellation(task)
// 将任务加入到队列中
schedule(now, task)
}
}
}
schedule方法内部最核心的执行会将任务加入到延迟队列中。这个延迟队列的内部是用数组实现的堆,延时任务在添加到队列时会按延迟时间排序。
每次调用EventLoopImplBase类对象的processNextEvent方法时,会优先判断延迟队列有没有任务,如果有,则继续判断延迟队列第一个任务是否满足时间要求,满足则从延迟队列队首取出,加入到任务队列队尾。之后从任务队列队首取出一个任务执行。
[]( )2.HandlerContext
----------------------------------------------------------------------------------
如果是在主线程执行协程时调用delay方法,则最后会通过HandlerContext调度器实现延迟,代码如下:
override fun scheduleResumeAfterDelay(timeMillis: Long, continuation: CancellableContinuation) {
// 将恢复续体执行的任务封装成Runnable
val block = Runnable {
with(continuation) { resumeUndispatched(Unit) }
}
// 延迟执行
handler.postDelayed(block, timeMillis.coerceAtMost(MAX_DELAY))
// 监听取消回调
continuation.invokeOnCancellation { handler.removeCallbacks(block) }
}
最终通过Handler实现的。
[]( )3.永久等待
------------------------------------------------------------------------
如果需要实现永远等待,可以使用awaitCancellation方法,该方法代码如下:
public suspend fun awaitCancellation(): Nothing = suspendCancellableCoroutine {}
该方法直接调用suspendCancellableCoroutine方法挂起协程,只有取消协程时才可以中断。
[]( )六.yield方法
===========================================================================
yield方法用于挂起当前协程的执行,并让出调度当前协程执行的线程,去执行其他的协程,代码如下:
// 获取续体
public suspend fun yield(): Unit = suspendCoroutineUninterceptedOrReturn sc@ { uCont ->
// 获取续体的上下文
val context = uCont.context
// 检查当前协程是否完成
context.checkCompletion()
// 获取拦截后的续体,转换成DispatchedContinuation类型的对象,转换失败则返回
val cont = uCont.intercepted() as? DispatchedContinuation<Unit> ?: return@sc Unit
// 判断是否需要调度
if (cont.dispatcher.isDispatchNeeded(context)) {
// 如果需要,则直接执行
cont.dispatchYield(context, Unit)
} else { // 如果不需要调度,可能立即去执行或为Unconfined调度器
// 创建一个YieldContext上下文,来检测Unconfined调度器
val yieldContext = YieldContext()
// 检测Unconfined调度器
cont.dispatchYield(context + yieldContext, Unit)
// 如果为Unconfined调度器
if (yieldContext.dispatcherWasUnconfined) {
// 调用为Unconfined调度器执行,成功则挂起
return@sc if (cont.yieldUndispatched()) COROUTINE_SUSPENDED else Unit
}
// 如果不是Unconfined调度器,则在dispatchYield方法中已经被调度了
}
// 返回挂起
COROUTINE_SUSPENDED
}
[]( )1.YieldContext类与Unconfined类
---------------------------------------------------------------------------------------------
YieldContext类是一个上下文对象,用来检测Unconfined调度器是否存在,其内部只有一个变量,代码如下:
internal class YieldContext : AbstractCoroutineContextElement(Key) {
companion object Key : CoroutineContext.Key<YieldContext>
// 默认为false
@JvmField
var dispatcherWasUnconfined = false
}
在上面的代码中,当调用DispatchedContinuation类的dispatchYield方法,会调用调度器的dispatchYield方法,根据[Kotlin协程:续体、续体拦截器、调度器]( )的分析,dispatchYield方法默认是通过dispatch方法实现的。因此最终会调用Unconfined调度器的dispatch方法。
Unconfined调度器不会将任务限制到指定的线程上执行。它的dispatch方法根本不会进行调度,所以isDispatchNeeded永远返回false,代码如下:
internal object Unconfined : CoroutineDispatcher() {
// 永远返回false
override fun isDispatchNeeded(context: CoroutineContext): Boolean = false
override fun dispatch(context: CoroutineContext, block: Runnable) {
// 获取YieldContext
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
总结
笔者之前工作是在金融公司可能并不是特别追求技术,而笔者又是喜欢追求技术的人,所以格格不入,只能把目标放在互联网大厂了。也希望大家都去敢于尝试和追逐自己的梦想!
BATJ大厂Android高频面试题
觉得有收获的记得点赞,关注+收藏哦!你们的点赞就是我的动力!
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
atchNeeded(context: CoroutineContext): Boolean = false
override fun dispatch(context: CoroutineContext, block: Runnable) {
// 获取YieldContext
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-R1sIggJ9-1712798435037)]
[外链图片转存中…(img-dr1BPfmY-1712798435038)]
[外链图片转存中…(img-i1UZUDhX-1712798435039)]
[外链图片转存中…(img-QwsM1Loz-1712798435039)]
[外链图片转存中…(img-LR9bb6Oy-1712798435039)]
[外链图片转存中…(img-ALd2jTuO-1712798435039)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-BUHMeUkT-1712798435040)]
总结
笔者之前工作是在金融公司可能并不是特别追求技术,而笔者又是喜欢追求技术的人,所以格格不入,只能把目标放在互联网大厂了。也希望大家都去敢于尝试和追逐自己的梦想!
BATJ大厂Android高频面试题
[外链图片转存中…(img-FDqVUjEJ-1712798435040)]
[外链图片转存中…(img-cFzQksXJ-1712798435040)]
[外链图片转存中…(img-UiMlbCI2-1712798435041)]
[外链图片转存中…(img-ZPbajwO2-1712798435041)]
觉得有收获的记得点赞,关注+收藏哦!你们的点赞就是我的动力!
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-Z7qUIMqV-1712798435041)]