您的位置:首页 > 移动开发 > Android开发

Android Kotlin协程入门

2021-09-26 17:09 1811 查看

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.ViewModel
import androidx.lifecycle.viewModelScope // 引入
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

class 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.kt

fun 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, "Unconfine
ad8
d 协程 ${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.kt
val 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)
}
}
}

看一下这个网络请求的流程

  1. 从主线程调用
    reqGet()
    函数
  2. viewModelScope.launch(Dispatchers.IO)
    在协程上发出网络请求
  3. 在协程中进行网络操作。把结果发送出去。

参考

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: