枯燥的Kotlin协程三部曲(中)——应用实战篇(下)

简介: 上节《枯燥的Kotlin协程三部曲(上)——概念启蒙篇》,追根溯源,先了解并发相关的概念,尔后引出Kotlin协程

⑤ 启动模式


launch & async那里截了launch和async的源码,关注第二个参数 CoroutineStart,点进源码:


public enum class CoroutineStart {
    // 默认,创建后立即开始调度,调度前被取消,直接进入取消响应状态。
    DEFAULT,
    // 懒加载,不会立即开始调度,需要手动调用start、join或await才会
    // 开始调度,如果调度前就被取消,协程将直接进入异常结束状态。
    LAZY,
    // 和Default类似,立即开始调度,在执行到一个挂起函数前不响应取消。
    // 涉及到cancle才有意义
    @ExperimentalCoroutinesApi
    ATOMIC,
    // 直接在当前线程执行协程体,直到遇到第一个挂起函数,才会调度到
    // 指定调度器所在的线程上执行
    @ExperimentalCoroutinesApi
    UNDISPATCHED;
}


0x8、调度器 → CoroutineDispatcher


① 四类调度器


Kotlin协程预置4种调度器,如下表所示:


种类 描述
Default 默认,线程池,适合处理后台计算,CPU密集型任务调度器
IO IO调度器,适合执行IO相关操作,IO密集型任务调度器
Main UI调度器,根据平台不同会初始化为对应UI线程的调度器,如Android的主线程
Unconfined 不指定线程,如果子协程切换线程,接下来的代码也在该线程继续执行


另外,调度器还提供了一个属性**immediate**,如果当前处于该调度器中,不执行调度器切换直接执行。对比示例如下:


CoroutineScope(Job() + Dispatchers.Main.immediate).launch {
  // 第一个执行
}
// 第二个执行
CoroutineScope(Job() + Dispatchers.Main).launch {
  // 调度器切换,导致慢一些所以第四个执行
}
// 第三个执行


② withContext


和launch、async及runBlocking不同,withContext不会创建新的协程,常用于 切换代码执行所运行的线程。 它也是一个挂起方法,直到结束返回结果。多个withContext是串行执行的, 所以很适合那种一个任务依赖上一个任务返回结果的情况,比如:


网络异常,图片无法展示
|


就很舒服,使用async+await可以是想同样的效果,只是需要创建了三个协程,有些多余:


网络异常,图片无法展示
|


0x9、拦截器 → ContinuationInterceptor


见名知意,就是用来拦截协程做一些 附加操作 的,比如上面的调度器,就用拦截器实现的。 写个拦截打日志的Demo试试水,这里的 Continuation 是用来保存协程挂起状态与局部变量的对象。


网络异常,图片无法展示
|


运行结果如下:


网络异常,图片无法展示
|


发生了4次拦截,依次是:协程启动时(前两个),挂起时返回结果时。 我们可以在写个简易版的线程调度器,让协程在启动时完成线程切换,示例如下:


网络异常,图片无法展示
|


运行结果如下:


网络异常,图片无法展示
|


可以看到,协程切换到自定义的线程池运行,配合withContext,运行完后又切回来了。


0x10、Deferred


看下Deferred的源码:


网络异常,图片无法展示
|


在继承Job的基础上,指定了<out T> 输出泛型,await()挂起协程并返回最后的执行结果。


0x11、Channel → 通道


与Java中用于解决多线程数据传递的BlockingQueue类似,Kotlin协程提供了 Channel, 用于解决多协程间的数据传递。元素从一端被加入,从另一端被消费,除了堵塞操作外, Channel还提供了非堵塞的send及receive操作。一个简单的使用代码示例如下:


网络异常,图片无法展示
|


另外,Receiver端支持用for迭代来接收消息,比如:for(c in channel) 也是可以的。


:用完Channel记得调用close()关闭通道,否则从Channel读取数据的协程都会无限挂起,在那里等数据传过来!!!


① 不同的Channel类型


打开Channel的源码:


网络异常,图片无法展示
|


继承了SendChannelReceiveChannel,然后定义了几个代表Channel类型的常量:


RENDEZVOUS → 默认,0缓存,创建了一个RendezvousChannel,send就挂起,直到被receive;
UNLIMITED → 创建了一个LinkedListChannel,无限容量,send不会挂起;
BUFFERED → 指定大小,创建了一个ArrayChannel,满了send会挂起;
CONFLATED → 创建了一个ConflatedChannel,新send的会覆盖之前send的,receiver只会得到最新的,send不会挂起;
// 创建不同类型Channel示例:
val rendezvousChannel = Channel<String>()
val unlimitedChannel = Channel<String>(UNLIMITED)
val bufferedChannel = Channel<String>(10)
val conflatedChannel = Channel<String>(CONFLATED)


② SendChannel & ReceiveChannel


SendChannel定义了往通道发送数据的接口,ReceiveChannel定义了从通道接收数据的接口,简单过下API:


/* === SendChannel === */
isClosedForSend: Boolean // 判断通道是否关闭,关闭了再发送数据会抛异常
isFull: Boolean          // 通道是否已满
close(cause: Throwable? = null): Boolean // 关闭通道
send(element: E)               // 挂起函数,通道满该函数会先暂停执行,如果通道关闭会抛异常
offer(element: E): Boolean     // 同步发送一个数据,通道满或关闭无法添加成功,返回false或抛异常
invokeOnClose(handler: (cause: Throwable?) -> Unit) // 通道关闭执行回调
onSend: SelectClause2<E, SendChannel<E>> //立即发送数据(如果允许), 在select表达式中使用
/* === ReceiveChannel === */
isClosedForReceive: Boolean // 判断通道是否关闭,关闭后还有缓存则会接收完再返回false
isEmpty: Boolean            // 通道是否为空
cancel(cause: CancellationException? = null)    // 关闭通道
receive(): E                // 挂起函数,接收数据,如果通道关闭会抛异常
receiveOrClosed(): ValueOrClosed<E> // 同上,只是通道关闭了不会抛异常,而是返回null
poll(): E                   // 从通道获取并删除一个数据,如果为空返回null,如果出错则抛出异常
onReceive: SelectClause1<E> // 用于select表达式


Channel、Flow以及实际应用后续补上,待续...


参考文献:



相关文章
|
2月前
|
Java 编译器 测试技术
Kotlin31 协程如何与 Java 进行混编?
Kotlin31 协程如何与 Java 进行混编?
34 2
Kotlin31 协程如何与 Java 进行混编?
|
2月前
|
存储 Kotlin
正则表达式在Kotlin中的应用:提取图片链接
正则表达式在Kotlin中的应用:提取图片链接
|
3月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
77 4
|
3月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
46 1
|
4月前
|
监控 安全 Java
Kotlin 在公司上网监控中的安卓开发应用
在数字化办公环境中,公司对员工上网行为的监控日益重要。Kotlin 作为一种基于 JVM 的编程语言,具备简洁、安全、高效的特性,已成为安卓开发的首选语言之一。通过网络请求拦截,Kotlin 可实现网址监控、访问时间记录等功能,满足公司上网监控需求。其简洁性有助于快速构建强大的监控应用,并便于后续维护与扩展。因此,Kotlin 在安卓上网监控应用开发中展现出广阔前景。
31 1
|
4月前
|
数据库 开发者 Python
实战指南:用Python协程与异步函数优化高性能Web应用
在快速发展的Web开发领域,高性能与高效响应是衡量应用质量的重要标准。随着Python在Web开发中的广泛应用,如何利用Python的协程(Coroutine)与异步函数(Async Functions)特性来优化Web应用的性能,成为了许多开发者关注的焦点。本文将从实战角度出发,通过具体案例展示如何运用这些技术来提升Web应用的响应速度和吞吐量。
39 1
|
4月前
|
Android开发 开发者 Kotlin
告别AsyncTask:一招教你用Kotlin协程重构Android应用,流畅度飙升的秘密武器
【9月更文挑战第13天】随着Android应用复杂度的增加,有效管理异步任务成为关键。Kotlin协程提供了一种优雅的并发操作处理方式,使异步编程更简单直观。本文通过具体示例介绍如何使用Kotlin协程优化Android应用性能,包括网络数据加载和UI更新。首先需在`build.gradle`中添加coroutines依赖。接着,通过定义挂起函数执行网络请求,并在`ViewModel`中使用`viewModelScope`启动协程,结合`Dispatchers.Main`更新UI,避免内存泄漏。使用协程不仅简化代码,还提升了程序健壮性。
132 1
|
4月前
|
数据采集 消息中间件 并行计算
进程、线程与协程:并发执行的三种重要概念与应用
进程、线程与协程:并发执行的三种重要概念与应用
97 0
|
4月前
|
调度 Python
python3 协程实战(python3经典编程案例)
该文章通过多个实战案例介绍了如何在Python3中使用协程来提高I/O密集型应用的性能,利用asyncio库以及async/await语法来编写高效的异步代码。
40 0
|
4月前
|
API 数据处理 数据库
掌握 Kotlin Flow 的艺术:让无限数据流处理变得优雅且高效 —— 实战教程揭秘如何在数据洪流中保持代码的健壮与灵活
Kotlin Flow 是一个强大的协程 API,专为处理异步数据流设计。它适合处理网络请求数据、监听数据库变化等场景。本文通过示例代码展示如何使用 Kotlin Flow 管理无限流,如实时数据流。首先定义了一个生成无限整数的流 `infiniteNumbers()`,然后结合多种操作符(如 `buffer`、`onEach`、`scan`、`filter`、`takeWhile` 和 `collectLatest`),实现对无限流的优雅处理,例如计算随机数的平均值并在超过阈值时停止接收新数据。这展示了 Flow 在资源管理和逻辑清晰性方面的优势。
84 0