zoukankan      html  css  js  c++  java
  • Android Kotlin协程入门

    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, "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.IODefault用的也是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. 在协程中进行网络操作。把结果发送出去。

    参考

    一个软件工程师的记录
  • 相关阅读:
    linux 命令汇总
    vue搭建环境并创建项目
    CentOS中利用Docker安装RabbitMQ
    CentOS中利用Docker安装Redis
    CentOS双机中Docker下安装Mysql并配置互为主从模式
    CentOS下挂载数据盘
    bootstrap 自适应和响应式布局的区别
    xampp lampp 改变网页root目录的方法
    vue 自适应 Responsive 设计
    vue的二维码生成器
  • 原文地址:https://www.cnblogs.com/rustfisher/p/15339044.html
Copyright © 2011-2022 走看看