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

目录
  • 一.协程的创建
    • 1.start方法
    • 2.CoroutineStart类
    • 3.startCoroutineCancellable方法
    • 4.createCoroutineUnintercepted方法
    • 5.createCoroutineFromSuspendFunction方法
  • 二.协程的启动
    • 1.ContinuationImpl类
    • 2.resumeCancellableWith方法
    • 3.BaseContinuationImpl类
    • 4.invokeSuspend方法
  • 三.协程的挂起与恢复

下面以launch方法为例进行分析。

一.协程的创建

launch方法的代码如下:

// CoroutineScope的扩展方法
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
}

newCoroutineContext用于计算新的上下文,代码如下:

public actual fun CoroutineScope.newCoroutineContext(context: CoroutineContext): CoroutineContext {
    // coroutineContext为CoroutineScope中保存的全局变量
    // 对上下文进行相加
    val combined = coroutineContext + context
    // 用于debug
    val debug = if (DEBUG) combined + CoroutineId(COROUTINE_ID.incrementAndGet()) else combined
    // 如果上下文中没有调度器,则添加一个默认的调度器
    return if (combined !== Dispatchers.Default && combined[ContinuationInterceptor] == null)
        debug + Dispatchers.Default else debug
}

1.start方法

在不指定协程启动模式的情况下,协程将按照DEFAULT模式启动,在上述代码中,会调用StandaloneCoroutine对象的start方法。StandaloneCoroutine的代码如下:

private open class StandaloneCoroutine(
    parentContext: CoroutineContext,
    active: Boolean
) : AbstractCoroutine<Unit>(parentContext, active) {
    override fun handleJobException(exception: Throwable): Boolean {
        handleCoroutineException(context, exception)
        return true
    }
}

StandaloneCoroutine类中仅重写了handleJobException方法,用于处理父协程不处理的异常。因此这里调用的start方法实际是父类AbstractCoroutine的方法,AbstractCoroutine类的start方法代码如下:

public fun <R> start(start: CoroutineStart, receiver: R, block: suspend R.() -> T) {
    // 该方法用于完成父协程与子协程的绑定关联,同时确保父协程启动
    initParentJob()
    // 该方法的写法等同于start.invoke(block, receiver, this)
    // 因此调用的CoroutineStart类的方法
    start(block, receiver, this)
}

AbstractCoroutine类的start方法内,调用了CoroutineStart类的invoke方法。

2.CoroutineStart类

CoroutineStart是一个枚举类,用于根据不同的启动模式去启动协程,代码如下:

public enum class CoroutineStart {
    // 四种启动模式
    DEFAULT,
    LAZY,
    // 具有实验性,慎用
    @ExperimentalCoroutinesApi
    ATOMIC,
    // 具有实验性,慎用
    @ExperimentalCoroutinesApi
    UNDISPATCHED;
    // 根据不同的启动策略,启动协程,执行block
    @InternalCoroutinesApi
    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 // 该模式不主动启动,等待用户调用start方法
        }
    // 根据不同的启动策略,启动协程,执行block
    @InternalCoroutinesApi
    public operator fun <R, T> invoke(block: suspend R.() -> T, receiver: R, completion: Continuation<T>): Unit =
        when (this) {
            DEFAULT -> block.startCoroutineCancellable(receiver, completion)
            ATOMIC -> block.startCoroutine(receiver, completion)
            UNDISPATCHED -> block.startCoroutineUndispatched(receiver, completion)
            LAZY -> Unit
        }
    // 当前的启动模式是否为懒启动
    @InternalCoroutinesApi
    public val isLazy: Boolean get() = this === LAZY
}

CoroutineStart类中有两个invoke方法,其中一个参数中有receiver,另一个没有receiver。在Kotlin协程中,很多方法都重载了带有receiver的方法和不带有receiver的方法。

receiver用于为block执行提供一个环境。Kotlin中提供的启动协程的方法都是通过带receiver参数的start方法实现。通过receiver环境,可以更方便的实现一些操作,比如在launch启动的协程中再次调用launch启动新的协程。在没有receiver的环境下执行block,则更像是在suspend方法中执行,如果需要启动其他的协程,需要自己提供环境。

3.startCoroutineCancellable方法

startCoroutineCancellable是一个扩展方法,用来创建一个可以取消的协程,代码如下:

internal fun <R, T> (suspend (R) -> T).startCoroutineCancellable(receiver: R, completion: Continuation<T>) =
    runSafely(completion) {
        // createCoroutineUnintercepted:创建协程
        // intercepted:拦截调度
        // resumeCancellableWith:恢复执行
        createCoroutineUnintercepted(receiver, completion).intercepted().resumeCancellableWith(Result.success(Unit))
    }
// 如果创建的过程发生异常,则通知续体恢复后续代码的执行
private inline fun runSafely(completion: Continuation<*>, block: () -> Unit) {
    try {
        block()
    } catch (e: Throwable) {
        completion.resumeWith(Result.failure(e))
    }
}

4.createCoroutineUnintercepted方法

createCoroutineUnintercepted方法用于创建一个新的、可挂起的、不受干扰的协程。

public expect fun <R, T> (suspend R.() -> T).createCoroutineUnintercepted(
    receiver: R,
    completion: Continuation<T>
): Continuation<Unit>

在Kotlin中有很多被expect关键字标记的接口方法,需要找到对应平台下被actual标记的实现方法。

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

createCoroutineUnintercepted方法创建的协程需要手动调用resumeWith方法才可以启动,但重复的调用resumeWith方法可能会导致状态机发生异常。同时,参数中传入的completion可能会在任意的上下文中被调用。

正常情况下,我们编写的lambda表达式——block,在编译器编译时,会自动生成一个类,并继承SuspendLambda类,实现Continuation等接口。因为SuspendLambda继承自ContinuationImpl,ContinuationImpl继承自BaseContinuationImpl,所以才有了上述代码中的判断逻辑。

如果当前的block对象的类型为BaseContinuationImpl,则调用create方法,这里的create方法是编译器生成的类里的重写方法,它的内部就是通过我们传入的参数,创建并返回根据blcok生成的类的一个实例对象。

如果当前的block对象的类型不为BaseContinuationImpl,则需要通过createCoroutineFromSuspendFunction方法创建协程。这里假设lambda表达式的类型不是BaseContinuationImpl。

5.createCoroutineFromSuspendFunction方法

该方法用于在createCoroutineUnintercepted方法中使用,当一个被suspend修饰的lambda表达式没有继承BaseContinuationImpl类时,则通过此方法创建协程。

有两种情况会调用该方法创建协程:第一种情况是lambda表达式中调用了其他的挂起方法;第二种情况是挂起方法是通过Java实现的。

createCoroutineFromSuspendFunction方法的代码如下:

private inline fun <T> createCoroutineFromSuspendFunction(
    completion: Continuation<T>,
    crossinline block: (Continuation<T>) -> Any?
): Continuation<Unit> {
    val context = completion.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()
                        block(this)
                    }
                    1 -> {
                        label = 2
                        result.getOrThrow()
                    }
                    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()
                        block(this)
                    }
                    1 -> {
                        label = 2
                        result.getOrThrow()
                    }
                    else -> error("This coroutine had already completed")
                }
        }
}

受限协程是指协程在运行过程中的,只能调用协程作用域中提供的挂起方法发生挂起,其他挂起方法不能调用,因为在挂起方法会对续体进行拦截,可能导致后续代码的执行变得无法预测。

典型的例子就是sequence方法,它创建的协程就是受限协程,只能通过调用yield方法或者yieldAll方法才能发生挂起。由于受限协程中不能进行协程调度,因此其上下文是空的。

这里launch方法的上下文有一个默认调度器,因此会创建一个ContinuationImpl对象。

到这里,协程完成了创建。

二.协程的启动

再次回到startCoroutineCancellable方法,当调用createCoroutineUnintercepted创建好协程后,会调用intercepted方法,代码如下:

public actual fun <T> Continuation<T>.intercepted(): Continuation<T> =
    (this as? ContinuationImpl)?.intercepted() ?: this

intercepted方法是Continuation接口的扩展方法,内部调用了ContinuationImpl类的intercepted方法。

1.ContinuationImpl类

internal abstract class ContinuationImpl(
    completion: Continuation<Any?>?,
    private val _context: CoroutineContext?
) : BaseContinuationImpl(completion) {
    constructor(completion: Continuation<Any?>?) : this(completion, completion?.context)
    public override val context: CoroutineContext
        get() = _context!!
    @Transient
    private var intercepted: Continuation<Any?>? = null
    // 如果没有缓存,则从上下文中获取拦截器,调用interceptContinuation进行拦截,
    // 将拦截的续体保存到全局变量
    public fun intercepted(): Continuation<Any?> =
        intercepted
            ?: (context[ContinuationInterceptor]?.interceptContinuation(this) ?: this)
                .also { intercepted = it }
    protected override fun releaseIntercepted() {
        val intercepted = intercepted
        if (intercepted != null && intercepted !== this) {
            context[ContinuationInterceptor]!!.releaseInterceptedContinuation(intercepted)
        }
        this.intercepted = CompletedContinuation // just in case
    }
}

这里的ContinuationInterceptor指的就是在newCoroutineContext方法中传入的Dispatchers.Default调度器。CoroutineDispatcher类的interceptContinuation方法的代码如下:

public abstract class CoroutineDispatcher :
    AbstractCoroutineContextElement(ContinuationInterceptor), ContinuationInterceptor {
     ...
    // 将续体包裹成DispatchedContinuation,并传入当前调度器
    public final override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
        DispatchedContinuation(this, continuation)
    ...
}

2.resumeCancellableWith方法

再次回到startCoroutineCancellable方法,当调用intercepted方法进行拦截后,会调用resumeCancellableWith方法,代码如下:

public fun <T> Continuation<T>.resumeCancellableWith(result: Result<T>): Unit = when (this) {
    is DispatchedContinuation -> resumeCancellableWith(result)
    else -> resumeWith(result)
}

由于当前的Continuation对象的类型为DispatchedContinuation,因此调用DispatchedContinuation类的resumeCancellableWith方法,代码如下:

internal class DispatchedContinuation<in T>(
    @JvmField val dispatcher: CoroutineDispatcher,
    @JvmField val continuation: Continuation<T>
) : DispatchedTask<T>(MODE_ATOMIC_DEFAULT), CoroutineStackFrame, Continuation<T> by continuation {
    ...
    @Suppress("NOTHING_TO_INLINE")
    inline fun resumeCancellableWith(result: Result<T>) {
        val state = result.toState()
        // 是否进行调度
        if (dispatcher.isDispatchNeeded(context)) {
            _state = state
            resumeMode = MODE_CANCELLABLE
            // 进行调度
            dispatcher.dispatch(context, this)
        } else {// Dispatcher.Unconfined调度器会走这里
            executeUnconfined(state, MODE_CANCELLABLE) {
                // 协程未被取消
                if (!resumeCancelled()) {
                    // 恢复执行
                    resumeUndispatchedWith(result)
                }
            }
        }
    }
    // 恢复执行前判断协程是否已经取消执行
    @Suppress("NOTHING_TO_INLINE")
    inline fun resumeCancelled(): Boolean {
        // 获取当前的协程任务
        val job = context[Job]
        // 如果不为空且不活跃
        if (job != null && !job.isActive) {
            // 抛出异常
            resumeWithException(job.getCancellationException())
            return true
        }
        return false
    }
    @Suppress("NOTHING_TO_INLINE")
    inline fun resumeUndispatchedWith(result: Result<T>) {
        // 该方法在指定的上下文中执行,在执行后同步协程上下文变化
        withCoroutineContext(context, countOrElement) {
            // 调用续体的resumeWith方法
            continuation.resumeWith(result)
        }
    }
    ...
}
// Dispatchers.Unconfined模式下的调度
private inline fun DispatchedContinuation<*>.executeUnconfined(
    contState: Any?, mode: Int, doYield: Boolean = false,
    block: () -> Unit
): Boolean {
    // 从ThreadLocal中获取EventLoop
    val eventLoop = ThreadLocalEventLoop.eventLoop
    // doYield表示是否正在让出执行
    // 如果正在让出执行,并且执行队列还是空的,说明不需要执行,返回false
    if (doYield && eventLoop.isUnconfinedQueueEmpty) return false
    // 如果EventLoop当前还在被Unconfined调度器使用
    return if (eventLoop.isUnconfinedLoopActive) {
        _state = contState
        resumeMode = mode
        // 向队列中添加当前的任务
        eventLoop.dispatchUnconfined(this)
        // 返回 true
        true
    } else {
        // 重新运行EventLoop
        runUnconfinedEventLoop(eventLoop, block = block)
        // 返回false
        false
    }
}

runUnconfinedEventLoop方法是一个扩展方法,用于启动EventLoop,代码如下:

internal inline fun DispatchedTask<*>.runUnconfinedEventLoop(
    eventLoop: EventLoop,
    block: () -> Unit
) {
    // 引用计数+1
    eventLoop.incrementUseCount(unconfined = true)
    try {
        // 先执行当前的任务
        block()
        // 循环分发任务
        while (true) {
            // 全部执行完毕,则退出分发
            if (!eventLoop.processUnconfinedEvent()) break
        }
    } catch (e: Throwable) {
        handleFatalException(e, null)
    } finally {
        // 引用计数+1
        eventLoop.decrementUseCount(unconfined = true)
    }
}

Dispatchers.Default调度器与Dispatchers.Unconfined调度器的调度逻辑基本都相同,最终都是调用Contination对象的resumeWith方法,同时传入Result对象作为参数。

这里的Contination是createCoroutineUnintercepted方法创建的继承ContinuationImpl的匿名内部类对象。Result是resumeCancellableWith方法传入的Result.success(Unit)对象,因为首次启动,所以传入类型为Unit。

调用匿名内部类的resumeWith方法,实际调用的是父类BaseContinuationImpl的resumeWith方法。

3.BaseContinuationImpl类

BaseContinuationImpl类的resumeWith方法的代码如下:

internal abstract class BaseContinuationImpl(
    public val completion: Continuation<Any?>?
) : Continuation<Any?>, CoroutineStackFrame, Serializable {
    public final override fun resumeWith(result: Result<Any?>) {
        var current = this
        var param = result
        // 循环
        while (true) {
            // 用于debug
            probeCoroutineResumed(current)
            // current环境下
            with(current) {
                // completion用于续体执行完的回调,为空,则抛出异常
                // 这里的completion就是一开始创建的StandaloneCoroutine对象
                val completion = completion!!
                // 获取执行后的结果
                val outcome: Result<Any?> =
                    try {
                        // 核心执行
                        val outcome = invokeSuspend(param)
                        // 如果返回值为COROUTINE_SUSPENDED,说明协程挂起,退出循环
                        if (outcome === COROUTINE_SUSPENDED) return
                        // 返回结果成功
                        Result.success(outcome)
                    } catch (exception: Throwable) {
                        // 返回结果失败
                        Result.failure(exception)
                    }
                // 释放拦截的续体,状态机终止
                releaseIntercepted()
                // 这里没有直接调用resume,而是通过循环代替递归
                // 这也是resumeWith方法声明为final的原因
                if (completion is BaseContinuationImpl) {
                    // 这种情况一般为多个suspend方法按顺序执行
                    // 等待下一次循环
                    current = completion
                    param = outcome
                } else {
                    // 返回结果
                    completion.resumeWith(outcome)
                    return
                }
            }
        }
    }
    protected abstract fun invokeSuspend(result: Result<Any?>): Any?
    protected open fun releaseIntercepted() {
        // does nothing here, overridden in ContinuationImpl
    }
    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")
    }
     ...
}

4.invokeSuspend方法

在上述代码中,resumeWith方法内部调用了invokeSuspend方法,这里的invokeSuspend方法实际就是createCoroutineFromSuspendFunction方法中创建的匿名内部类的invokeSuspend方法。匿名内部类的代码如下:

object : ContinuationImpl(completion as Continuation<Any?>, context) {
    // 初始状态
    private var label = 0
    override fun invokeSuspend(result: Result<Any?>): Any? =
            when (label) {
                0 -> {
                    label = 1
                    // 先去获取一次结果,如果有异常,则直接抛出,避免执行
                    // 比如在调度器中,如果发现协程已经取消,
                    // 则调用resumeWithException方法,在这里直接被抛出
                    result.getOrThrow()
                    // 把当前续体传入,执行协程
                    // 可能发生挂起
                    block(this)
                }
                1 -> {
                    // 如果协程发生了挂起,那么恢复挂起后会走到这里
                    label = 2
                    // 获取最终的执行结果
                    result.getOrThrow()
                }
                else -> error("This coroutine had already completed")
            }
}

三.协程的挂起与恢复

通过上述代码的分析,协程的挂起实际就是在协程返回结果时返回一个COROUTINE_SUSPENDED对象,在收到COROUTINE_SUSPENDED结果后直接返回,等待被再次调用resumeWith恢复。

COROUTINE_SUSPENDED对象定义在枚举类CoroutineSingletons中,代码如下:

internal enum class CoroutineSingletons { COROUTINE_SUSPENDED, UNDECIDED, RESUMED }

该枚举类代表了协程的三个状态,协程在创建后状态为UNDECIDED,如果执行过程中发生挂起,则状态变为COROUTINE_SUSPENDED,最后挂起恢复后状态变为RESUMED。

而协程的恢复实际就是在挂起方法执行完成后,通过调用协程执行时传入的续体的resumeWith方法,恢复后续代码的执行。

到此这篇关于Kotlin协程操作之创建启动挂起恢复详解的文章就介绍到这了,更多相关Kotlin协程操作内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 利用Kotlin的协程实现简单的异步加载详解

    前言 众所周知在android中当执行程序的耗时超过5秒时就会引发ANR而导致程序崩溃.由于UI的更新操作是在UI主线程进行的,理想状态下每秒展示60帧时人眼感受不到卡顿,1000ms/60帧,即每帧绘制时间不应超过16.67ms.如果某项操作的耗时超过这一数值就会导致UI卡顿.因此在实际的开发中我通常把耗时操作放在一个新的线程中(比如从网络获取数据,从SD卡读取图片等操作),但是呢在android中UI的更新只能在UI主线程中进行更新,因此当我们在非UI线程中执行某些操作的时候想要更新UI就需

  • Kotlin协程上下文与上下文元素深入理解

    目录 一.EmptyCoroutineContext 二.CombinedContext 三.Key与Element 四.CoroutineContext 五.AbstractCoroutineContextKey与AbstractCoroutineContextElement 一.EmptyCoroutineContext EmptyCoroutineContext代表空上下文,由于自身为空,因此get方法的返回值是空的,fold方法直接返回传入的初始值,plus方法也是直接返回传入的cont

  • kotlin之协程的理解与使用详解

    前言         为什么在kotlin要使用协程呢,这好比去了重庆不吃火锅一样的道理.协程的概念并不陌生,在python也有提及.任何事务的作用大多是对于所依赖的环境相应而生的,协程对于kotlin这门语言也不例外.协程的优点,总的来说有如下几点:轻量级,占用更少的系统资源: 更高的执行效率: 挂起函数较于实现Runnable或Callable接口更加方便可控: kotlin.coroutine 核心库的支持,让编写异步代码更加简单.当然在一些不适应它的用法下以上优势也会成为劣势. 1.协程

  • Kotlin学习教程之协程Coroutine

    定义 Coroutine翻译为协程,Google翻译为协同程序,一般也称为轻量级线程,但需要注意的是线程是操作系统里的定义概念,而协程是程序语言实现的一套异步处理的方法. 在Kotlin文档中,Coroutine定义为一个可被挂起的计算实例,下面话不多说了,来一起看看详细的介绍吧. 配置 build.gradle中dependencies 添加下面2行,注意coroutine目前仍处于experiment阶段,但Kotline官方保证向前兼容. dependencies { implementa

  • 一文彻底搞懂Kotlin中的协程

    产生背景 为了解决异步线程产生的回调地狱 //传统回调方式 api.login(phone,psd).enquene(new Callback<User>(){ public void onSuccess(User user){ api.submitAddress(address).enquene(new Callback<Result>(){ public void onSuccess(Result result){ ... } }); } }); //使用协程后 val use

  • Kotlin创建一个好用的协程作用域

    目录 前言 正文 前言 kotlin中使用协程,是一定要跟协程作用域一起配合使用的,否则可能协程的生命周期无法被准确控制,造成内存泄漏或其他问题. 我们一般在安卓项目中使用协程作用域,可能会在BaseActtivity中new 一个MainScope(),并在onDestory时cancel掉,或者只在ViewModel中使用viewModelScope,然后会在ViewModel的onClose中自动cancel掉. 但我们可能不只需要这些效果,比如在协程作用域中拿到Context或Activ

  • Kotlin协程概念原理与使用万字梳理

    目录 一.协程概述 1.概念 2.特点 3.原理 二.协程基础 1.协程的上下文 2.协程的作用域 3.协程调度器 4.协程的启动模式 5.协程的生命周期 三.协程使用 1.协程的启动 2.协程间通信 3.多路复用 4.序列生成器 5.协程异步流 6.全局上下文 一.协程概述 1.概念 协程是Coroutine的中文简称,co表示协同.协作,routine表示程序.协程可以理解为多个互相协作的程序.协程是轻量级的线程,它的轻量体现在启动和切换,协程的启动不需要申请额外的堆栈空间:协程的切换发生在

  • Kotlin中协程的创建过程详析

    目录 为什么需要协程? 创建并启动协程 协程的执行过程 suspend block 是如何变为协程体被执行的? 总结 总结 为什么需要协程? 协程可以简化异步编程,可以顺序地表达程序,协程也提供了一种避免阻塞线程并用更廉价.更可控的操作替代线程阻塞的方法 – 挂起函数. Kotlin 的协程是依靠编译器实现的, 并不需要操作系统和硬件的支持.编译器为了让开发者编写代码更简单方便, 提供了一些关键字(例如suspend), 并在内部自动生成了一些支持型的代码. 创建并启动协程 fun create

  • Kotlin协程到底是如何切换线程的

    随着kotlin在Android开发领域越来越火,协程在各个项目中的应用也逐渐变得广泛 但是协程到底是什么呢? 协程其实是个古老的概念,已经非常成熟了,但大家对它的概念一直存在各种疑问,众说纷纷 有人说协程是轻量级的线程,也有人说kotlin协程其实本质是一套线程切换方案 显然这对初学者不太友好,当不清楚一个东西是什么的时候,就很难进入为什么和怎么办的阶段了 本文主要就是回答这个问题,主要包括以下内容 1.关于协程的一些前置知识 2.协程到底是什么? 3.kotlin协程的一些基本概念,挂起函数

  • 使用kotlin协程提高app性能(译)

    协程是一种并发设计模式,您可以在Android上使用它来简化异步执行的代码.Kotlin1.3版本添加了 Coroutines,并基于其他语言的既定概念. 在Android上,协程有助于解决两个主要问题: 管理长时间运行的任务,否则可能会阻止主线程并导致应用冻结. 提供主安全性,或从主线程安全地调用网络或磁盘操作. 本主题描述了如何使用Kotlin协程解决这些问题,使您能够编写更清晰,更简洁的应用程序代码. 管理长时间运行的任务 在Android上,每个应用程序都有一个主线程来处理用户界面并管理

  • 一篇文章揭开Kotlin协程的神秘面纱

    前言 Kotlin协程提供了一种新的异步执行方式,但直接查看库函数可能会有点混乱,本文中尝试揭开协程的神秘面纱. 理论 它是什么 这是别人翻译: 协程把异步编程放入库中来简化这类操作.程序逻辑在协程中顺序表述,而底层的库会将其转换为异步操作.库会将相关的用户代码打包成回调,订阅相关事件,调度其执行到不同的线程(甚至不同的机器),而代码依然想顺序执行那么简单. 我的理解:子任务程协作运行,优雅的处理异步问题解决方案. 它能干什么? 我在做安卓开发,它能替换掉Handler,AsyncTask 甚至

  • Python协程操作之gevent(yield阻塞,greenlet),协程实现多任务(有规律的交替协作执行)用法详解

    本文实例讲述了Python 协程操作之gevent(yield阻塞,greenlet),协程实现多任务(有规律的交替协作执行)用法.分享给大家供大家参考,具体如下: 实现多任务:进程消耗的资源最大,线程消耗的资源次之,协程消耗的资源最少(单线程). gevent实现协程,gevent是通过阻塞代码(例如网络延迟等)来自动切换要执行的任务,所以在进行IO密集型程序时(例如爬虫),使用gevent可以提高效率(有效利用网络延迟的时间去执行其他任务). GIL(全局解释器锁)是C语言版本的Python

  • DBA_Oracle Startup / Shutdown启动和关闭过程详解(概念)(对数据库进行各种维护操作)

    一.摘要 Oracle数据库的完整启动过程是分步骤完成的,包含以下3个步骤: 启动实例-->加载数据库-->打开数据库 因为Oracle数据库启动过程中不同的阶段可以对数据库进行不同的维护操作,对应我们不同的需求,所以就需不同的模式启动数据库. 1. Oracle启动需要经历四个状态:SHUTDOWN .NOMOUNT .MOUNT .OPEN 2. Oracle关闭的四种方式:Normal, Immediate, Transactional, Abort 3. 启动和关闭过程详解   二.数

  • IOS 创建并发线程的实例详解

    IOS 创建并发线程的实例详解 创建并发线程 主线程一般都是处理UI界面及用户交互的事儿的.其他的事一般就要另外的线程去处理,如下载,计算等... 现在先简单创建3个线程,分别打印出1-1000,,为了方便,线程3就放在主线程中执行. - (void) firstCounter{ @autoreleasepool { NSUInteger counter = 0; for (counter = 0; counter < 1000; counter++){ NSLog(@"First Cou

  • Python命令启动Web服务器实例详解

    Python命令启动Web服务器实例详解 利用Python自带的包可以建立简单的web服务器.在DOS里cd到准备做服务器根目录的路径下,输入命令: python -m Web服务器模块 [端口号,默认8000] 例如: python -m SimpleHTTPServer 8080 然后就可以在浏览器中输入 http://localhost:端口号/路径 来访问服务器资源. 例如: http://localhost:8080/index.htm(当然index.htm文件得自己创建) 其他机器

  • Android 两种启动模式的实例详解

    Android 两种启动模式的实例详解 Intent的FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT Activity的两种启动模式:FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_REORDER_TO_FRONT 1. 如果已经启动了四个Activity:A,B,C和D.在D Activity里,我们要跳到B Activity,同时希望C finish掉,可以在startActivity(intent)里

  • Python之使用adb shell命令启动应用的方法详解

    一直有一个心愿希望可以用Python做安卓自动化功能测试,在一步步摸索中,之前是用monkeyrunner,但是发现对于控件ID的使用非常具有局限性,尤其是ID的内容不便于区分 具有重复性时,后面又发现Uiautomator可以对resorceId.text.packageName等元素进行定位,也找到了xiaochong这位大神关于uiautomator的封装包,链接如下: https://github.com/xiaocong/uiautomator 做为一个小白,这一切都需要摸索,在克服了

  • Git 创建分支提交远程分支详解

    Git 创建分支提交远程分支详解 1.创建本地分支 git branch 分支名,例如:git branch 2.0.1.20120806 注:2.0.1.20120806是分支名称,可以随便定义. 2.切换本地分支 git checkout 分支名,例如从master切换到分支:git checkout 2.0.1.20120806 3.远程分支就是本地分支push到服务器上.比如master就是一个最典型的远程分支(默认). git push origin 2.0.1.20120806 4.

  • mysql创建删除表的实例详解

    表的创建命令需要: 表的名称 字段名称 定义每个字段(类型.长度等) 语法 下面是通用的SQL语法用来创建MySQL表: CREATE TABLE table_name (column_name column_type); 现在,我们将在 test 数据库中创建以下表. create table tutorials_tbl( tutorial_id INT NOT NULL AUTO_INCREMENT, tutorial_title VARCHAR(100) NOT NULL, tuto

随机推荐

其他