阿里P7大佬教你破解 Kotlin 协程(6) - 协程挂起篇

跨平台app开发框架首选,android组件化和插件化 需要注意 onMeasure,onLayout,onDraw 的调用时机,以及 Touch 事件分发机制。 阅读详情

破解 Kotlin 协程(6) - 协程挂起篇

在这里插入图片描述

关键词:Kotlin 协程 协程挂起 任务挂起 suspend 非阻塞

协程的挂起最初是一个很神秘的东西,因为我们总是用线程的概念去思考,所以我们只能想到阻塞。不阻塞的挂起到底是怎么回事呢?说出来你也许会笑~~(哭?。。抱歉这篇文章我实在是没办法写的更通俗易懂了,大家一定要亲手实践!)

1. 先看看 delay

我们刚刚学线程的时候,最常见的模拟各种延时用的就是 Thread.sleep 了,而在协程里面,对应的就是 delay。 sleep 让线程进入休眠状态,直到指定时间之后某种信号或者条件到达,线程就尝试恢复执行,而 delay 会让协程挂起,这个过程并不会阻塞 CPU,甚至可以说从硬件使用效率上来讲是“什么都不耽误”,从这个意义上讲 delay 也可以是让协程休眠的一种很好的手段。

delay 的源码其实很简单:

public suspend fun delay(timeMillis: Long) {
    if (timeMillis <= 0) return // don't delay
    return suspendCancellableCoroutine sc@ { cont: CancellableContinuation<Unit> ->
        cont.context.delay.scheduleResumeAfterDelay(timeMillis, cont)
    }
}

cont.context.delay.scheduleResumeAfterDelay 这个操作,你可以类比 JavaScript 的 setTimeout,Android 的 handler.postDelay,本质上就是设置了一个延时回调,时间一到就调用 cont 的 resume 系列方法让协程继续执行。

剩下的最关键的就是 suspendCancellableCoroutine 了,这可是我们的老朋友了,前面我们用它实现了回调到协程的各种转换 —— 原来 delay 也是基于它实现的,如果我们再多看一些源码,你就会发现类似的还有 join、 await 等等。

2. 再来说说 suspendCancellableCoroutine

既然大家对于 suspendCancellableCoroutine 已经很熟悉了,那么我们干脆直接召唤一个老朋友给大家:

private suspend fun joinSuspend() = suspendCancellableCoroutine<Unit> { cont ->
    cont.disposeOnCancellation(invokeOnCompletion(handler = ResumeOnCompletion(this, cont).asHandler))
}

Job.join() 这个方法会首先检查调用者 Job 的状态是否已经完成,如果是,就直接返回并继续执行后面的代码而不再挂起,否则就会走到这个 joinSuspend 的分支当中。我们看到这里只是注册了一个完成时的回调,那么传说中的 suspendCancellableCoroutine 内部究竟做了什么呢?

public suspend inline fun <T> suspendCancellableCoroutine(
    crossinline block: (CancellableContinuation<T>) -> Unit
): T =
    suspendCoroutineUninterceptedOrReturn { uCont ->
        val cancellable = CancellableContinuationImpl(uCont.intercepted(), resumeMode = MODE_CANCELLABLE)
        block(cancellable)
        cancellable.getResult() // 这里的类型是 Any?
    }

suspendCoroutineUninterceptedOrReturn 这个方法调用的源码是看不到的,因为它根本没有源码:P 它的逻辑就是帮大家拿到 Continuation 实例,真的就只有这样。不过这样说起来还是很抽象,因为有一处非常的可疑: suspendCoroutineUninterceptedOrReturn 的返回值类型是 T,而传入的 lambda 的返回值类型是 Any?, 也就是我们看到的 cancellable.getResult() 的类型是 Any?,这是为什么?

我记得在协程系列文章的开篇,我就提到过 suspend 函数的签名,当时是以 await 为例的,这个方法大致相当于:

fun await(continuation: Continuation<User>): Any {
    ...
}

suspend 一方面为这个方法添加了一个 Continuation 的参数,另一方面,原先的返回值类型 User 成了 Continuation 的泛型实参,而真正的返回值类型竟然是 Any。当然,这里因为定义的逻辑返回值类型 User是不可空的,因此真实的返回值类型也用了 Any 来示意,如果泛型实参是个可空的类型,那么真实的返回值类型也就是 Any? 了,这正与前面提到的 cancellable.getResult() 返回的这个 Any? 相对应。

如果大家去查 await 的源码,你同样会看到这个 getResult() 的调用。

简单来说就是,对于 suspend 函数,不是一定要挂起的,可以在需要的时候挂起,也就是要等待的协程还没有执行完的时候,等待协程执行完再继续执行;而如果在开始 join 或者 await 或者其他 suspend 函数,如果目标协程已经完成,那么就没必要等了,直接拿着结果走人即可。那么这个神奇的逻辑就在于 cancellable.getResult() 究竟返回什么了,且看:

internal fun getResult(): Any? {
    ...
    if (trySuspend()) return COROUTINE_SUSPENDED // ① 触发挂起逻辑
    ...
    if (state is CompletedExceptionally)  // ② 异常立即抛出
        throw recoverStackTrace(state.cause, this) 
    return getSuccessfulResult(state) // ③ 正常结果立即返回
}

这段代码 ① 处就是挂起逻辑了,表示这时候目标协程还没有执行完,需要等待结果,②③是协程已经执行完可以直接拿到异常和正常结果的两种情况。②③好理解,关键是 ①,它要挂起,这返回的是个什么东西?

public val COROUTINE_SUSPENDED: Any get() = CoroutineSingletons.COROUTINE_SUSPENDED

internal enum class CoroutineSingletons { COROUTINE_SUSPENDED, UNDECIDED, RESUMED }

这是 1.3 的实现,1.3 以前的实现更有趣,就是一个白板 Any。其实是什么不重要,关键是这个东西是一个单例,任何时候协程见到它就知道自己该挂起了。

3. 深入挂起操作

既然说到挂起,大家可能觉得还是一知半解,还是不知道挂起究竟怎么做到的,怎么办?说真的这个挂起是个什么操作其实一直没有拿出来给大家看,不是我们太小气了,只是太早拿出来会比较吓人。。

suspend fun hello() = suspendCoroutineUninterceptedOrReturn<Int>{
    continuation ->
    log(1)
    thread {
        Thread.sleep(1000)
        log(2)
        continuation.resume(1024)
    }
    log(3)
    COROUTINE_SUSPENDED
}

我写了这么一个 suspend 函数,在 suspendCoroutineUninterceptedOrReturn 当中直接返回了这个传说中的白板 COROUTINE_SUSPENDED,正常来说我们应该在一个协程当中调用这个方法对吧,可是我偏不,我写一段 Java 代码去调用这个方法,结果会怎样呢?

public class CallCoroutine {
    public static void main(String... args) {
        Object value = SuspendTestKt.hello(new Continuation<Integer>() {
            @NotNull
            @Override
            public CoroutineContext getContext() {
                return EmptyCoroutineContext.INSTANCE;
            }

            @Override
            public void resumeWith(@NotNull Object o) { // ①
                if(o instanceof Integer){
                    handleResult(o);
                } else {
                    Throwable throwable = (Throwable) o;
                    throwable.printStackTrace();
                }
            }
        });

        if(value == IntrinsicsKt.getCOROUTINE_SUSPENDED()){ // ②
            LogKt.log("Suspended.");
        } else {
            handleResult(value);
        }
    }

    public static void handleResult(Object o){
        LogKt.log("The result is " + o);
    }
}

这段代码看上去比较奇怪,可能会让人困惑的有两处:

① 处,我们在 Kotlin 当中看到的 resumeWith 的参数类型是 Result,怎么这儿成了 Object 了?因为 Result 是内联类,编译时会用它唯一的成员替换掉它,因此就替换成了 Object (在Kotlin 里面是 Any?)

② 处 IntrinsicsKt.getCOROUTINE_SUSPENDED() 就是 Kotlin 的 COROUTINE_SUSPENDED

剩下的其实并不难理解,运行结果自然就是如下所示了:

07:52:55:288 [main] 1
07:52:55:293 [main] 3
07:52:55:296 [main] Suspended.
07:52:56:298 [Thread-0] 2
07:52:56:306 [Thread-0] The result is 1024

其实这段 Java 代码的调用方式与 Kotlin 下面的调用已经很接近了:

suspend fun main() {
    log(hello())
}

只不过我们在 Kotlin 当中还是不太容易拿到 hello 在挂起时的真正返回值,其他的返回结果完全相同。

12:44:08:290 [main] 1
12:44:08:292 [main] 3
12:44:09:296 [Thread-0] 2
12:44:09:296 [Thread-0] 1024

很有可能你看到这里都会觉得晕头转向,没有关系,我现在已经开始尝试揭示一些协程挂起的背后逻辑了,比起简单的使用,概念的理解和接受需要有个小小的过程。

4. 深入理解协程的状态转移

前面我们已经对协程的原理做了一些揭示,显然 Java 的代码让大家能够更容易理解,那么接下来我们再来看一个更复杂的例子:

suspend fun returnSuspended() = suspendCoroutineUninterceptedOrReturn<String>{
    continuation ->
    thread {
        Thread.sleep(1000)
        continuation.resume("Return suspended.")
    }
    COROUTINE_SUSPENDED
}

suspend fun returnImmediately() = suspendCoroutineUninterceptedOrReturn<String>{
    log(1)
    "Return immediately."
}

我们首先定义两个挂起函数,第一个会真正挂起,第二个则会直接返回结果,这类似于我们前面讨论 join 或者 await 的两条路径。我们再用 Kotlin 给出一个调用它们的例子:

suspend fun main() {
    log(1)
    log(returnSuspended())
    log(2)
    delay(1000)
    log(3)
    log(returnImmediately())
    log(4)
}

运行结果如下:

08:09:37:090 [main] 1
08:09:38:096 [Thread-0] Return suspended.
08:09:38:096 [Thread-0] 2
08:09:39:141 [kotlinx.coroutines.DefaultExecutor] 3
08:09:39:141 [kotlinx.coroutines.DefaultExecutor] Return immediately.
08:09:39:141 [kotlinx.coroutines.DefaultExecutor] 4

好,现在我们要揭示这段协程代码的真实面貌,为了做到这一点,我们用 Java 来仿写一下这段逻辑:

注意,下面的代码逻辑上并不能做到十分严谨,不应该出现在生产当中,仅供学习理解协程使用。

public class ContinuationImpl implements Continuation<Object> {

    private int label = 0;
    private final Continuation<Unit> completion;

    public ContinuationImpl(Continuation<Unit> completion) {
        this.completion = completion;
    }

    @Override
    public CoroutineContext getContext() {
        return EmptyCoroutineContext.INSTANCE;
    }

    @Override
    public void resumeWith(@NotNull Object o) {
        try {
            Object result = o;
            switch (label) {
                case 0: {
                    LogKt.log(1);
                    result = SuspendFunctionsKt.returnSuspended( this);
                    label++;
                    if (isSuspended(result)) return;
                }
                case 1: {
                    LogKt.log(result);
                    LogKt.log(2);
                    result = DelayKt.delay(1000, this);
                    label++;
                    if (isSuspended(result)) return;
                }
                case 2: {
                    LogKt.log(3);
                    result = SuspendFunctionsKt.returnImmediately( this);
                    label++;
                    if (isSuspended(result)) return;
                }
                case 3:{
                    LogKt.log(result);
                    LogKt.log(4);
                }
            }
            completion.resumeWith(Unit.INSTANCE);
        } catch (Exception e) {
            completion.resumeWith(e);
        }
    }

    private boolean isSuspended(Object result) {
        return result == IntrinsicsKt.getCOROUTINE_SUSPENDED();
    }
}

我们定义了一个 Java 类 ContinuationImpl,它就是一个 Continuation 的实现。

实际上如果你愿意,你还真得可以在 Kotlin 的标准库当中找到一个名叫 ContinuationImpl 的类,只不过,它的 resumeWith 最终调用到了 invokeSuspend,而这个 invokeSuspend 实际上就是我们的协程体,通常也就是一个 Lambda 表达式 —— 我们通过 launch启动协程,传入的那个 Lambda 表达式,实际上会被编译成一个 SuspendLambda 的子类,而它又是 ContinuationImpl 的子类。

有了这个类我们还需要准备一个 completion 用来接收结果,这个类仿照标准库的 RunSuspend 类实现,如果你有阅读前面的文章,那么你应该知道 suspend main 的实现就是基于这个类:

public class RunSuspend implements Continuation<Unit> {

    private Object result;

    @Override
    public CoroutineContext getContext() {
        return EmptyCoroutineContext.INSTANCE;
    }

    @Override
    public void resumeWith(@NotNull Object result) {
        synchronized (this){
            this.result = result;
            notifyAll(); // 协程已经结束,通知下面的 wait() 方法停止阻塞
        }
    }

    public void await() throws Throwable {
        synchronized (this){
            while (true){
                Object result = this.result;
                if(result == null) wait(); // 调用了 Object.wait(),阻塞当前线程,在 notify 或者 notifyAll 调用时返回
                else if(result instanceof Throwable){
                    throw (Throwable) result;
                } else return;
            }
        }
    }
}

这段代码的关键点在于 await() 方法,它在其中起了一个死循环,不过大家不要害怕,这个死循环是个纸老虎,如果 result 是 null,那么当前线程会被立即阻塞,直到结果出现。具体的使用方法如下:

...
    public static void main(String... args) throws Throwable {
        RunSuspend runSuspend = new RunSuspend();
        ContinuationImpl table = new ContinuationImpl(runSuspend);
        table.resumeWith(Unit.INSTANCE);
        runSuspend.await();
    }
...

这写法简直就是 suspend main 的真实面貌了。

我们看到,作为 completion 传入的 RunSuspend 实例的 resumeWith 实际上是在 ContinuationImpl 的 resumeWtih 的最后才会被调用,因此它的 await() 一旦进入阻塞态,直到 ContinuationImpl 的整体状态流转完毕才会停止阻塞,此时进程也就运行完毕正常退出了。

于是这段代码的运行结果如下:

08:36:51:305 [main] 1
08:36:52:315 [Thread-0] Return suspended.
08:36:52:315 [Thread-0] 2
08:36:53:362 [kotlinx.coroutines.DefaultExecutor] 3
08:36:53:362 [kotlinx.coroutines.DefaultExecutor] Return immediately.
08:36:53:362 [kotlinx.coroutines.DefaultExecutor] 4

我们看到,这段普通的 Java 代码与前面的 Kotlin 协程调用完全一样。那么我这段 Java 代码的编写根据是什么呢?就是 Kotlin 协程编译之后产生的字节码。当然,字节码是比较抽象的,我这样写出来就是为了让大家更容易的理解协程是如何执行的,看到这里,相信大家对于协程的本质有了进一步的认识:

  • 协程的挂起函数本质上就是一个回调,回调类型就是 Continuation
  • 协程体的执行就是一个状态机,每一次遇到挂起函数,都是一次状态转移,就像我们前面例子中的 label 不断的自增来实现状态流转一样

如果能够把这两点认识清楚,那么相信你在学习协程其他概念的时候就都将不再是问题了。如果想要进行线程调度,就按照我们讲到的调度器的做法,在 resumeWith 处执行线程切换就好了,其实非常容易理解的。官方的协程框架本质上就是在做这么几件事儿,如果你去看源码,可能一时云里雾里,主要是因为框架除了实现核心逻辑外还需要考虑跨平台实现,还需要优化性能,但不管怎么样,这源码横竖看起来就是五个字:状态机回调。

5. 小结

不同以往,我们从这一篇开始毫无保留的为大家尝试揭示协程背后的逻辑,也许一时间可能有些难懂,不过不要紧,你可以使用协程一段时间之后再来阅读这些内容,相信一定会豁然开朗的。

Kotlin 协程学习资料可以扫描下方二维码免费领取!

# 《**史上最详Android版kotlin协程入门进阶实战** **》**

第一章 Kotlin协程的基础介绍

​ ● 协程是什么

​ ● 什么是Job 、Deferred 、协程作用域

​ ● Kotlin协程的基础用法

img

第二章 kotlin协程的关键知识点初步讲解

​ ● 协程调度器

​ ● 协程上下文

​ ● 协程启动模式

​ ● 协程作用域

​ ● 挂起函数

img

第三章 kotlin协程的异常处理

​ ● 协程异常的产生流程

​ ● 协程的异常处理

img

第四章 kotlin协程在Android中的基础应用

​ ● Android使用kotlin协程

​ ● 在Activity与Framgent中使用协程

​ ● ViewModel中使用协程

​ ● 其他环境下使用协程

img

第五章 kotlin协程的网络请求封装

​ ● 协程的常用环境

​ ● 协程在网络请求下的封装及使用

​ ● 高阶函数方式

​ ● 多状态函数返回值方式

论文阅读:arixv 2025 Poisoning Attacks on LLMs Require a Near-constant Number of Poison Samples 研究者做了目前最大规模的“投毒实验”,训练了从6亿参数到130亿参数的模型(覆盖了中小到大型LLM),用的训练数据量也是“最适合模型大小”的(按行业里的“Chinchilla法则”,模型越大,训练数据量也按比例增加)。总结下来就是:这篇论文告诉我们,给大语言模型投毒,比之前想的容易得多——不需要海量毒数据,只要几百条藏好后门的文档,就能让大模型“带毒工作”,而且平时还看不出来。——这比之前大家担心的“需要投毒数据占训练集一定比例”要可怕得多,因为对大模型来说,“固定数量”的毒数据更容易偷偷塞进去。 阅读详情

相关推荐

python爬虫入门教程(非常详细)

爬虫指的是一种自动化程序,能够模拟人类在互联网上的浏览行为,自动从互联网上抓取、预处理并保存所需要的信息。爬虫运行的过程一般是先制定规则(如指定要抓取的网址、要抓取的信息的类型等),紧接着获取该网址的HTML源代码,根据规则对源代码进行解析和抽取,最后进行处理和保存。爬虫在实际应用中广泛使用,如搜索引擎、大数据分析、交易数据采集等领域,都需要用到爬虫技术来实现信息的定向采集和处理。

工宗浩生财指南针 2万+

Kotlin学习九:kotlin中的异步操作(wait(), notify() 和 notifyAll())

我(原作者)最近开始阅读G. Blake Meike的“Android Concurrency”,到目前为止,我可以强烈推荐这本伟大的书:它包含了很多洞察如何各种Android并发机制的工作,何时喜欢一种方法,以及如何获得最好的工具,在您的处置。我决定遵循代码示例,并在我的电脑上重新创建它们,并且由于我非常爱Kotlin,我认为这将是一个好主意,将示例转换为Kotlin在旅途中。当我开始重写Kotlin中的代码示例时,我非常惊讶地发现: Kotlin中没有synchronized关键字 Kotlin中没有v

lixpjita39的专栏 6774

Keil5安装多版本共存方案:实用技巧分享

分享Keil5安装过程中实现多版本共存的实用方法,解决开发环境冲突问题。通过合理配置路径与版本管理,让keil5安装更高效稳定,提升嵌入式开发体验。

weixin_42514750的博客 1534

Kotlin - 协程中的那些关键点

写在前面 1.协程“非阻塞式挂起” 你真的弄懂了吗? 2.协程suspend关键字到底有多神奇? 3.协程有了launch,还要async干啥?抱小三? 4.协程真的高大上吗? 1.非阻塞式挂起 阻塞:即程序因耗时操作等原因阻止线程继续运行 非阻塞:即不阻止程序继续运行 挂起:即wait,释放已经获取到的线程资源 非阻塞式挂起:不组织程序运行,还释放了已经获取到的线程资源 那么问题来了,你把资源都释放了我咋继续后续操作? Are you kid me? 他到底干了啥,有人说是关键字suspend,但是但是,

crazymaple213的专栏 327

保姆级教程:用Labelme标注车辆关键点,并一键转成YOLOv8-pose训练格式

本文提供了一份详细的车辆底盘关键点标注教程,从使用Labelme进行标注到一键转换为YOLOv8-pose训练格式的全流程指南。特别针对工业检测和自动驾驶场景,分享了标注规范、格式转换脚本及常见问题解决方案,帮助开发者高效准备训练数据并优化模型性能。

weixin_27419879的博客 309

海上AI导航系统:技术架构与行业应用解析

计算机视觉与强化学习技术在海上导航系统中的应用正逐步改变传统航运业。通过多模态传感器融合技术,现代AI导航系统能够实现高精度的物体识别与实时路径规划,有效解决能见度依赖、决策延迟等核心痛点。以Orca AI的SeaPod系统为例,其结合了NVIDIA GeForce RTX 4070的边缘计算能力与多光谱协同识别技术,在恶劣环境下仍能保持稳定性能。这类系统不仅提升了航行安全性,还通过优化航线显著降低燃油消耗,在东京湾、马六甲海峡等繁忙水域展现出巨大价值。随着毫米波雷达融合方案的测试推进,未来海上导航将突破极

weixin_33597080的博客 355

Kotlin 协程的挂起和阻塞的区别

Kotlin协程引入了非常强大的异步编程模型,通过挂起而不是阻塞来实现并发操作。挂起(Suspending):挂起是指一个协程的执行可以在不阻塞线程的情况下暂停和恢复。挂起函数是一种能够让协程挂起并释放线程的特殊函数,允许其他协程在该协程挂起期间运行。协程可以在执行IO操作、等待网络请求、休眠或执行任何可能导致阻塞的操作时挂起。阻塞:阻塞是指线程在执行某个操作时被暂停,直到该操作完成,而不能执行其他任务。

ezconn的博客 4011

使用SSC添加大量的SDO条目

使用SSC添加大量的SDO条目 SSC用表格添加SDO,PDO部分必须指定到0x1600和0x1A00分配TxPdo和RxPdo映射,在0x1C12和0x1C13分别分配到SM2和SM3。在0x6000和0x7000未指定的entry就是SDO部分。

PI_sunyang的博客 3168

【Java】多线程编程(一)

宽泛地说明了何为线程、线程的中断、线程锁与同步、竞态条件、重入锁与条件对象 和volatile用法,旨在帮助读者对线程有宏观的了解和清晰的认识。

Frms的博客 1184

全面解析 SAP 事务码 LTMOM 的应用与实践

综上所述,事务码 LTMOM 是 SAP 系统中不可或缺的迁移对象维护工具,它不仅提供了对数据迁移对象的精细配置功能,还通过灵活的数据转换规则与异常监控机制,为企业数据从 SAP ERP 向 SAP S/4HANA 的迁移提供了坚实保障。在数据迁移过程中,并非所有的迁移对象都需要同时启用。业内专家普遍认为,未来的系统升级与数据迁移将更注重业务需求的动态变化与数据实时性的要求,而 LTMOM 作为核心工具,其在流程自动化、数据智能校验以及跨系统数据联动方面的能力,将直接影响企业整体数字化转型的成败。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1013

TIA Portal (博图) -两台不同网段S7-1200通信(上)

因为不同网段意味着它们默认不在同一个广播域,并且没有网关设备来处理路由,所以标准的 TCP/IP 通信(如 `TSEND_C`/`TRCV_C`)无法直接建立连接。S7 通信 (PUT/GET): 最简单,配置好连接后,在客户端 PLC 使用 `PUT` 指令发送数据到服务器 PLC,使用 `GET` 指令从服务器 PLC 读取数据。* 在地址详细信息中,确保 本地连接端点是客户端 PLC 的接口,远程连接端点是服务器 PLC 的接口和 IP (`192.168.2.20`)。

电工 2684

kotlin协程

协程是什么 协程的概念其实很早之前就有了,并非Kotlin提出来的。百度一下发现有很多的所谓比较正式的定义,但是理解起来我骂娘。最多的是那协程与线程做比对的,得出的结论大多是协程和线程一样是用来实现异步任务的,异步也就是不阻塞当前的线程,可是线程都是顺序执行的,这就导致一个矛盾的问题出现,所以你查到的文章写得其实都不是真确的!其实协程最关键的知识就是调度器,调度器可以启动一个协程,调度器的原理知

欢乐斗佛的博客 925

18、动态系统响应分析

本文系统地介绍了动态系统响应的分析方法,涵盖传递函数建模、特殊输入下的响应特性(如脉冲和阶跃响应)、终值与初值定理的应用、系统直流增益的计算,并深入探讨了一阶和二阶系统的时域特性。通过实例分析与MATLAB仿真,展示了不同参数对系统行为的影响。文章还总结了系统响应在控制设计与故障诊断中的实际应用,并展望了多变量、非线性及实时响应分析的未来研究方向。配合流程图,提供了清晰的系统分析与设计路径。

kite3的博客 143

阿里P7大佬教你破解 Kotlin 协程(7) - 序列生成器篇

​ ● 协程是什么​ ● 什么是Job 、Deferred 、协程作用域​ ● Kotlin协程的基础用法。

Android_XG的博客 211

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

阿里P7大佬教你破解 Kotlin 协程(3) - 协程调度篇

有了前面的基础,我们对于调度器的介绍就变得水到渠成了。......它本身是协程上下文的子类,同时实现了拦截器的接口,dispatch方法会在拦截器的方法中调用,进而实现协程的调度。所以如果我们想要实现自己的调度器,继承这个类就可以了,不过通常我们都用现成的,它们定义在这个类的定义涉及到了 Kotlin MPP 的支持,因此你在 Jvm 版本当中还会看到,在 js 和 native 当中就只有前面提到的这三个了(对 Jvm 好偏心呐)。JvmJsNativeDefault线程池。

Android_XG的博客 371

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

阿里P7大佬教你破解 Kotlin 协程(5) - 协程取消篇

​ ● 协程是什么​ ● 什么是Job 、Deferred 、协程作用域​ ● Kotlin协程的基础用法。

Android_XG的博客 535

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

P7阿里大佬教你破解 Kotlin 协程(10) - Select 篇

在协程当中,Select 的语义与 Java NIO 或者 Unix 的 IO 多路复用类似,它的存在使得我们可以轻松实现 1 拖 N,实现哪个先来就处理哪个。尽管 Select 和 Channel 比起标准库的协程 API 已经更接近业务开发了,不过个人认为它们仍属于相对底层的 API 封装,在实践当中多数情况下也可以使用 Flow API 来解决。而这个 Flow API,完全就是响应式编程的协程版 API,我们简直可以照着 RxJava 来学习它,所以我们下一篇再见吧~~~,这样反而将问题复杂化了。

Android_XG的博客 183

阿里P7大佬教你破解 Kotlin 协程(4) - 异常处理篇

​ ● 协程是什么​ ● 什么是Job 、Deferred 、协程作用域​ ● Kotlin协程的基础用法[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直

Android_XG的博客 491

阿里P7大佬教你 Kotlin 协程(1) - 入门篇

Kotlin 协程(1) - 入门篇

Android_XG的博客 354

分享两道阿里P7究极难度算法题,快来收藏!

缘起 随着互联网企业的不断发展,产品项目中的模块越来越多,用户体验要求也越来越高,想实现小步快跑、快速迭代的目的越来越难,还有应用之间的互相调用等等问题,插件化技术应用而生。如果没有插件化技术,美团、淘宝这些集成了大量“app”的应用,可能会有几个g那么大。 所以,当今的Android移动开发,不会热修复、插件化、组件化,80%以上的面试都过不了。 阿里P8大佬每天熬夜到凌晨一两点,花了将近半个月时间将Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、

左夜天 139

阿里P7亲自讲解!Android开发者值得深入思考的几个问题,使用指南

O(∩_∩)O~今天又给大家带来了21道Android高级面试题,搞懂了这些相信你去面试的时候一定可以镇住面试官 为此我把这些知识整理成了一个983页PDF,从基础到进阶。含有BATJ.字节跳动面试专题,算法专题,高端技术专题,混合开发专题,java面试专题,Android,Java小知识,到性能优化线程.View.OpenCV.NDK等应有尽有。还有辅之相关的视频+学习笔记 Java相关 容器(HashMap、HashSet、LinkedList、ArrayList、数组等) 内存模型 .

m0_57081438的博客 154

原理竟然是这!分享两道阿里P7究极难度算法题,快来收藏!

缘起 随着互联网企业的不断发展,产品项目中的模块越来越多,用户体验要求也越来越高,想实现小步快跑、快速迭代的目的越来越难,还有应用之间的互相调用等等问题,插件化技术应用而生。如果没有插件化技术,美团、淘宝这些集成了大量“app”的应用,可能会有几个g那么大。 所以,当今的Android移动开发,不会热修复、插件化、组件化,80%以上的面试都过不了。 阿里P8大佬每天熬夜到凌晨一两点,花了将近半个月时间将Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应式编程框架、

m0_56255252的博客 161

kotlin编程实践pdf,Jetpack-MVVM-高频提问和解答

自我介绍一下,小编13年北京科技大学毕业,曾经在字节跳动待过,也去过vivo、小米等大厂,18年进入华为一直到现在。小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担,能在岗位上再创辉煌,步步高升!!。

2401_84903191的博客 699

v1.4 发布在即,诞生近 10 年的 Kotlin 取代 Java 了吗?

热文推荐:尘埃落定!清华才子王垠加入华为职级22,前阿里P10赵海平加入字节跳动,职级或为4+百度网盘“破解版”,Pandownload开发者被抓Kotlin 诞生于 2010 年,出自...

Java和Android架构 927

安卓系统应用程序开发!灵魂一问-如何彻底防止APK反编译?大厂面试题汇总

缘起 随着互联网企业的不断发展,产品项目中的模块越来越多,用户体验要求也越来越高,想实现小步快跑、快速迭代的目的越来越难,还有65535,应用之间的互相调用等等问题,插件化技术应用而生。如果没有插件化技术,美团、淘宝这些集成了大量“app”的应用,可能会有几个g那么大。 所以,当今的Android移动开发,不会热修复、插件化、组件化,80%以上的面试都过不了。 阿里P8大佬每天熬夜到凌晨一两点,花了将近半个月时间将Android热修复框架、插件化框架、组件化框架、图片加载框架、网络访问框架、RxJava响应

335

Android开发工程师技术栈与面试全解析

Android开发作为移动互联网的核心技术领域,其技术栈涵盖从语言特性到系统架构的多层次知识。Kotlin已成为主流开发语言,但Java的JVM原理和并发模型仍是性能优化的关键基础。现代Android开发强调声明式UI(如Jetpack Compose)与响应式架构(如MVVM),同时需要掌握内存管理、线程优化等底层机制。在工程实践层面,模块化设计、CI/CD流水线和性能监控体系构建是高级工程师的核心能力。大厂面试往往深度考察Handler机制、自定义View优化等系统原理,以及解决复杂工程问题的实战经验。

weixin_32667955的博客 321
上一篇: 三面阿里Android 高开岗,复习两月有幸拿到 offer
下一篇: 2023年,Android开发不仅要苟住,看懂这篇Android Framework精编内核解析还要跳槽涨薪
不秃头de程序猿
博客等级 码龄5年 4089粉丝 1478原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值