Android官网举荐应用协程来解决异步问题。
协程的特点:
- 轻量:单个线程上可运行多个协程。协程反对挂起,不会使正在运行协程的线程阻塞。挂起比阻塞节俭内存,且反对多个并行操作。
- 内存透露更少:应用结构化并发机制在一个作用域内执行多项操作。
- 内置勾销反对:勾销操作会主动在运行中的整个协程层次结构内流传。
- Jetpack集成:许多Jetpack库都蕴含提供全面协程反对的扩大。某些库还提供本人的协程作用域,可用于结构化并发。
示例
首先工程中须要引入 Kotlin 与协程。而后再应用协程发动网络申请。
引入:
Android 工程中引入 Kotlin,参考 Android 我的项目应用 kotlin
有了Kt后,引入协程
dependencies { implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" // 协程}
启动协程
不同于 Kotlin 工程间接应用 GlobalScope,这个示例在ViewModel中应用协程。须要应用viewModelScope
。
上面的 CorVm1 继承了 ViewModel。
import androidx.lifecycle.ViewModelimport androidx.lifecycle.viewModelScope // 引入import kotlinx.coroutines.Dispatchersimport kotlinx.coroutines.launchclass CorVm1 : ViewModel() { companion object { const val TAG = "rfDevCorVm1" } fun cor1() { viewModelScope.launch { Log.d(TAG, "不指定dispatcher ${Thread.currentThread()}") } }}
在按钮的点击监听器中调用 cor1() 办法,能够看到协程是在主线程中的。
不指定dispatcher Thread[main,5,main]
因为此协程通过viewModelScope
启动,因而在 ViewModel 的作用域内执行。如果 ViewModel 因用户来到屏幕而被销毁,则viewModelScope
会主动勾销,且所有运行的协程也会被勾销。
launch()
办法能够指定运行的线程。能够传入Dispatchers
来指定运行的线程。
先简略看一下kotlinx.coroutines
包里的 Dispatchers ,它有4个属性:
Default
,默认Main
,Android中指定的是主线程Unconfined
,不指定线程IO
,指定IO线程
都通过点击事件来启动
// CorVm1.ktfun ioCor() { viewModelScope.launch(Dispatchers.IO) { Log.d(TAG, "IO 协程 ${Thread.currentThread()}") }}fun defaultCor() { viewModelScope.launch(Dispatchers.Default) { Log.d(TAG, "Default 协程 ${Thread.currentThread()}") }}fun mainCor() { viewModelScope.launch(Dispatchers.Main) { Log.d(TAG, "Main 协程 ${Thread.currentThread()}") }}fun unconfinedCor() { viewModelScope.launch(Dispatchers.Unconfined) { Log.d(TAG, "Unconfined 协程 ${Thread.currentThread()}") }}
运行log
IO 协程 Thread[DefaultDispatcher-worker-1,5,main]Main 协程 Thread[main,5,main]Default 协程 Thread[DefaultDispatcher-worker-1,5,main]Unconfined 协程 Thread[main,5,main]
从下面的比拟能够看出,如果想利用后盾线程,能够思考Dispatchers.IO
。Default
用的也是DefaultDispatcher-worker-1
线程。
模仿网络申请
主线程中不能进行网络申请,咱们把申请放到为IO操作预留的线程上执行。一些信息用 MutableLiveData 收回去。
// CorVm1.ktval info1LiveData: MutableLiveData<String> = MutableLiveData()private fun reqGet() { info1LiveData.value = "发动申请" viewModelScope.launch(Dispatchers.IO) { val url = URL("https://www.baidu.com/s?wd=abc") try { val conn = url.openConnection() as HttpURLConnection conn.requestMethod = "GET" conn.connectTimeout = 10 * 1000 conn.setRequestProperty("Cache-Control", "max-age=0") conn.doOutput = true val code = conn.responseCode if (code == 200) { val baos = ByteArrayOutputStream() val inputStream: InputStream = conn.inputStream val inputS = ByteArray(1024) var len: Int while (inputStream.read(inputS).also { len = it } > -1) { baos.write(inputS, 0, len) } val content = String(baos.toByteArray()) baos.close() inputStream.close() conn.disconnect() info1LiveData.postValue(content) Log.d(TAG, "net1: $content") } else { info1LiveData.postValue("网络申请出错 $conn") Log.e(TAG, "net1: 网络申请出错 $conn") } } catch (e: Exception) { Log.e(TAG, "reqGet: ", e) } }}
看一下这个网络申请的流程
- 从主线程调用
reqGet()
函数 viewModelScope.launch(Dispatchers.IO)
在协程上收回网络申请- 在协程中进行网络操作。把后果发送进来。
kotlin 协程相干知识点
1. 协程根底
- 你的第一个协程程序
- 桥接阻塞与非阻塞的世界
- 期待一个工作
- 结构化的并发
- 作用域构建器
- 提取函数重构
- ......
2. 勾销与超时
- 勾销协程的执行
- 勾销是合作的
- 使计算代码可勾销
- 在 finally 中开释资源
- 运行不能取消的代码块
- 超时
3. 通道
- 通道根底
- 敞开与迭代通道
- 构建通道生产者
- 管道
- 应用管道的素数
- 扇出
- 扇入
- 带缓冲的通道
- 通道是偏心的
- 计时器通道
4. 组合挂起函数
- 默认顺序调用
- 应用 async 并发
- 惰性启动的 async
- async 格调的函数
- 应用 async 的结构化并发
5. 协程上下文与调度器
- 调度器与线程
- 非受限调度器 vs 受限调度器
- 调试协程与线程
- 在不同线程间跳转
- 上下文中的工作
- 子协程
- 父协程的职责
- 命名协程以用于调试
- 组合上下文中的元素
- 通过显式工作勾销
- 线程部分数据
6. 异样解决
- 异样的流传
- CoroutineExceptionHandler
- 勾销与异样
- 异样聚合
- 监督
7. select 表达式
- 在通道中 select
- 通道敞开时 select
- Select 以发送
- Select 提早值
- 在提早值通道上切换
8. 共享的可变状态与并发
- 问题
- volatile 杯水车薪
- 线程平安的数据结构
- 以细粒度限度线程
- 以粗粒度限度线程
- 互斥
- Actors
【Kotlin入门到精通全系列视频参考】