Kotlin Coroutine(协程): 一、样例
@
前言
你还在用 Hanlder + Message? 或者 AsyncTask? 你还在用 Rxjava?
有人说Rxjava和Coroutine是从不同维度解决异步, 并且Rxjava的强大不止于异步问题.
好吧, 管它呢. 让我们拥抱 Coroutine(协程) 吧.
协程的篇幅有点长, 需要自己编写大量的测试例子. 从入门到都学完, 难度还是挺大的. 所以:我们直接把例子摆出来. 至于其他的, 我们从浅到深,日后..日后..再说 [奸笑]

Kotlin中文站: 请看官网
准备:
lifecycle 都有吧? 例子中需要用到 lifecycleScope;
//coroutines: 协程
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.3"
//coroutines for Android
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.3'
//lifecycle
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.2.0'
没有lifecycle 怎么办? 当页面销毁时,那些延时任务, 例如Handler, 网络请求 啥的, 需要及时关闭, 它们会影响 Activity 的及时销毁, 带来内存泄漏的风险. 协程也是如此.
此时我们必须自己管理协程的作用域; 所以最好还是用 lifecycleScope.
//自己定义作用域, 页面销毁时, 取消协程. 替换lifecycleScope即可;
val mainScope = MainScope()
override fun onDestroy(){
super.onDestroy()
mainScope.cancel()
}
什么是协程作用域呢? 类似于生命周期, 当页面销毁时, 协程应当随之停止.
提示:以下是本篇文章正文内容,下面案例可供参考
一、直接上例子
协程能干嘛? 延时任务, 异步任务, 定时任务, 并行任务, 协程+Retrofit. 异步流? 通道?
1.延时任务.
比较常见的业务场景, 几秒钟后执行...
还记得handler怎么写吗? 定义 Handler 实现类, 然后
mHandler.sendMessageDelayed()
//或者
mHandler.postDelayed(Runnable{...})
使用协程:
doDelayed()
fun doDelayed() = lifecycleScope.launch {
delay(2000L) //协程挂起2秒
binding.tvTitle.text = "变变变, 我是百变小魔女"
}
- lifecycleScope: 默认主线程. 它会监听Activity生命周期, 不需要手动 cancel()
- launch: 非阻塞形式启动协程.
- delay(2000): 挂起协程, 2秒后恢复.
然后封装一下:
//执行延时任务
fun CoroutineScope.doDelayed(timeMillis: Long, block: suspend () -> Unit) = this.launch {
delay(timeMillis)
block.invoke()
}
//使用
lifecycleScope.doDelayed(2000L){
binding.tvTitle.text = "变变变, 我是百变小魔女"
}
2.异步任务
在子线程执行任务, 完事回调主线程
代码如下:
doOnBack()
// 模拟子线程中执行了2秒钟的任务,
private fun doOnBack() = lifecycleScope.launch {
val result = withContext(Dispatchers.IO){
delay(2000) // 假装努力干活中..
"变变变, 我是百变小魔女"
}
binding.tvTitle.text = result
}
//或者. async .
private fun doOnBack() = lifecycleScope.launch {
val deferred = async (Dispatchers.IO){
delay(2000) // 假装努力干活中..
"变变变, 我是百变小魔女"
}
binding.tvTitle.text = deferred.await()
}
- withContext(): 不新建协程, 它只指定 执行当前代码块 所需的线程;
- async: 创建一个协程; 它返回一个Deferred, 而lanuch返回Job对象.
多个async可以支持并发, await()是等待任务执行完毕,并返回结果. - Dispatchers.IO: 协程调度器. 几种调度器如下所示
| 参数 | 意义 |
|---|---|
| 不指定 | 它从启动了它的 CoroutineScope 中承袭了上下文 |
| Dispatchers.Main | 用于Android. 在UI线程中执行 |
| Dispatchers.IO | 子线程, 适合执行磁盘或网络 I/O操作 |
| Dispatchers.Default | 子线程,适合 执行 cpu 密集型的工作 |
| Dispatchers.Unconfined | 管它呢, 不常用, 先不管 |
由于异步任务需要两段代码块. 子线程代码,及主线程响应; 所以就不封装了; 并且里面其实就一句 withContext(Dispatchers.IO) 或者 val deferred = async (Dispatchers.IO)
3.并行任务:
有时多个任务异步进行, 而我们不清楚哪一个会先完成, 我们需要等待它们的最终结果时.
private suspend fun taskOne(): Int {
delay(2000) //模拟任务执行 2秒
return 1
}
private suspend fun taskTwo(): Int {
delay(1500) //模拟任务执行 1.5秒
return 2
}
private suspend fun taskExecute(): Int = coroutineScope {
val result1 = async(Dispatchers.IO) { taskOne() }
val result2 = async(Dispatchers.IO) { taskTwo() }
result1.await() + result2.await()
}
//使用
lifecycleScope.launch {
val sum = taskExecute()
binding.tvTitle.text = "变变变, 我是百变小魔女$sum"
}
taskOne 执行需要2秒; taskTwo 执行需要1.5秒; 并行, 任务总共耗费约 2秒时间.
suspend: 标志挂起函数, 挂起函数只能在协程中运行.
4.定时任务:
定时任务简单啊, delay()不阻塞线程啊.
lifecycleScope.launch {
repeat(Int.MAX_VALUE){
delay(1000L)
binding.tvTitle.text = "变变变, 我是百变小魔女$it"
}
}
这个定时任务, 每次执行, 只设置TextView的text.
我们假设我们的业务比较复杂, 每次需要耗费300ms; 如下所示:
lifecycleScope.launch {
repeat(Int.MAX_VALUE){
delay(1000L)
//假设我们的任务比较繁重, 每次需要消耗300ms
delay(300L)
binding.tvTitle.text = "变变变, 我是百变小魔女$it"
}
}
显而易见, 此时至少要 1300ms 才能循环一次. 所以上面写的1000ms只能算是间隔时间. 而任务执行需要时间, 协程挂起,恢复需要时间, 任务进入队列到线程执行也需要时间.
因此:
当定时任务的精确度要求不高, 每次执行的代码任务比较轻便. 耗时较少时, 可以用这种方式.
有的时候, 我们锁屏了, 或者跳到别的页面了, 我们不需要定时一直执行, 即便更新了UI, 我们也看不到啊! 所以,我们希望页面离开时, 定时取消. 页面显示时,重新启动定时即可:
我们就以秒杀倒计时为例;
private var endTime: Long = 0 //秒杀截止时间
//秒杀剩余 5分钟
endTime = System.currentTimeMillis() + 300 * 1000
lifecycleScope.launchWhenResumed { //onPause 的时候会暂停.
repeat(Int.MAX_VALUE){
val now = System.currentTimeMillis()
binding.tvTitle.text = if(now >= endTime){
"秒杀结束"
}else{
val second = (endTime - now) / 1000 + 1
"秒杀倒计时($second); it=$it"
}
delay(1000L)
}
}
launchWhenResumed: 当页面 Resumed 时, 启动协程. onPause()时自动停止. 页面重新显示时恢复. 通过观察这里的 it, 可以断定 协程只是被挂起, 而不是销毁重建.
同样的还有: launchWhenCreated; launchWhenStarted 当然这是 lifecycle 的知识了.
有时, 我们需要精确的定时器.
可以用, java.util 包下的 Timer, 如下所示; 也可以用 CountDownTimer
val timer = timer("定时Thread_name", false, 2000, 1000){
letUsPrintln("定时器, 这是个子线程")
}
当然, 它没有自动暂停恢复的功能; 取消执行时, 别忘了 cancel();
协程+Retrofit 的封装还是放到后面吧..
总结
没啥好总结的, 复制粘贴就完事了. 下一篇我们再深入浅出的了解协程 [奸笑]
上一篇: Room-数据持久化存储(进阶)
下一篇: Kotlin Coroutine(协程): 二、初识协程
Kotlin Coroutine(协程): 一、样例的更多相关文章
- Kotlin Coroutine(协程): 二、初识协程
@ 目录 前言 一.初识协程 1.runBlocking: 阻塞协程 2.launch: 创建协程 3.Job 4.coroutineScope 5.协程取消 6.协程超时 7.async 并行任务 ...
- Kotlin Coroutine(协程): 三、了解协程
@ 目录 前言 一.协程上下文 1.调度器 2.给协程起名 3.局部变量 二.启动模式 CoroutineStart 三.异常处理 1.异常测试 2.CoroutineExceptionHandler ...
- Kotlin Coroutine(协程): 四、+ Retrofit
@ 目录 前言 一.准备工作 二.开始使用 1.简单使用 2.DSL 3.扩展函数 4.请求发起 总结 前言 Retrofit 从 2.6.0 版本开始, 内置了对 Kotlin Coroutines ...
- Coroutine(协程)模式与线程
概念 协程(Coroutine)这个概念最早是Melvin Conway在1963年提出的,是并发运算中的概念,指两个子过程通过相互协作完成某个任务,用它可以实现协作式多任务,协程(coroutine ...
- Android中的Coroutine协程原理详解
前言 协程是一个并发方案.也是一种思想. 传统意义上的协程是单线程的,面对io密集型任务他的内存消耗更少,进而效率高.但是面对计算密集型的任务不如多线程并行运算效率高. 不同的语言对于协程都有不同的实 ...
- coroutine协程
如果你接触过lua这种小巧的脚本语言,你就会经常接触到一个叫做协程的神奇概念.大多数脚本语言都有对协程不同程度的支持.但是大多编译语言,如C/C++,根本就不知道这样的东西存在.当然也很多人研究如何在 ...
- [Unity-22] Coroutine协程浅析
1.概念解释 协程并非一个独立的线程.在Unity中.全部的语句都是在一个线程中运行的,也就是说.Unity是单线程的(详细的能够參见http://blog.csdn.net/alexander_xf ...
- Coroutine 协程
https://en.wikipedia.org/wiki/Coroutine Coroutines are computer program components that generalize s ...
- 利用swoole coroutine协程实现redis异步操作
<?php #注意:如果不开启兼容模式,会遇到这样的现象,用swoole协程的方法访问常规方法添加到redis中的数据,可能访问不到(直接返回NULL)!这可能是两者采用了不同的技术标准所致! ...
随机推荐
- wxPython开发之密码管理程序
不想记密码?密码全设置成一样担心安全?用别人程序担心密码泄露?看完本博客,开发一个属于自己的密码管理程序吧 我们用到的是python的wxPython界面库包 先来看下成果界面:简洁主题明确 要想开 ...
- Ansible学习分享(基本)
背景:Teamleader提到一款好用的自动化配置管理工具,于是前去学习实践,有了下面分享. 纲要 一.Ansible简介 二.Ansible准备 2.1 Ansible安装 2.2 设置SSH公钥验 ...
- 自定义 DataLoader
自定义 DataLoader 如 数据输入 一文所介绍,OneFlow 支持两种数据加载方式:直接使用 NumPy 数据或者使用 DataLoader 及其相关算子. 在大型工业场景下,数据加载容易成 ...
- Docker Context基本原理
Docker Context基本原理 介绍 本指南介绍了上下文如何使单个Docker CLI轻松管理多个Swarm集群.多个Kubernetes集群和多个单独的Docker节点. 单个Docker C ...
- Timer定时器开发
Timer定时器开发 定时器的作用是不占线程的等待一个确定时间,同样通过callback来通知定时器到期. 参考:https://github.com/sogou/workflow 定时器的创建 同样 ...
- Json文件解析(下)
Json文件解析(下) 代码地址:https://github.com/nlohmann/json 从STL容器转换 任何序列容器(std::array,std::vector,std::dequ ...
- python工业互联网应用实战18—前后端分离模式之jquery vs vue
前面我们分三章来说明了使用django template与jquery的差别,通过jquery如何来实现前后端的分离,同时再9章节使用vue.js 我们浅尝辄止的介绍了JQuery到vue的切换,由于 ...
- ES6深拷贝与浅拷贝
今天小编和大家一起探讨js中深拷贝和浅拷贝,简单一点理解就是,对于引用数据类型,深拷贝是数据引用地址不同,在改变一个数据的时候,不会影响另一个数据.而浅拷贝刚好相反.两个数据引用的是同一个堆内存地址, ...
- C# 24点游戏求解算法
经常跟儿子玩24点,有时候比较难算的,算一会儿,两人算不出来,就收了,当作没法算. 以我的数学能力,一般来说,算不出来的,大概率确实是算不出来的. 但是遇到比较变态的,当作算不出来是可能的,所以一直想 ...
- Spring Boot WebFlux-08——WebFlux 中 WebSocket 实现通信
第08课:WebFlux 中 WebSocket 实现通信 前言 WebFlux 该模块中包含了对反应式 HTTP.服务器推送事件和 WebSocket 的客户端和服务器端的支持.这里我们简单实践下 ...