IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Kotlin协程(一) -> 正文阅读

[移动开发]Kotlin协程(一)

一、前言

? 在kotlin中协程用法是比较广泛的,协程也是比较复杂的,本文只对其做个简单的记录,后面再进行详细描述

二、线程与协程的区别

? 由于有些耗时操作需要等待,因此常常放在子线程中去操作,用来提升用户体验。但是线程存在一些以下问题(这些问题其实可以解决,只是比较麻烦),所以kotlin建议使用协程来处理异步任务

  • 线程需要大量的资源。

    但是因为线程的创建、切换和管理线程会占用系统资源和时间,从而限制能够同时管理的原始线程数量。创建成本也会大幅增加。

  • 不可预测的行为

    线程是一种抽象表示,用于说明处理器如何同时处理多个任务。由于处理器会在不同线程上的各个指令集之间切换,因此您无法控制线程的确切执行时间和暂停时间。直接使用线程时,您不能总是期望能生成可预测的输出。

  • 竞态条件

    在使用多个线程时,您可能还会遇到“竞态条件”。这是指多个线程尝试同时访问内存中的同一个值的情况。竞态条件会导致出现难以重现且看起来随机的错误,这样的错误可能会导致应用崩溃(通常是不可预测的)。

协程能够处理多任务,但比直接使用线程更为抽象。协程的一项重要功能是能够存储状态,以便协程可以暂停和恢复。协程可以执行,也可以不执行。

借助状态(由“连续性”表示),部分代码可以在需要移交控制权或需要等待其他协程完成后才能恢复时发出信号。此流程称为“协作式多任务处理”。Kotlin 的协程实现增加了一些协助处理多任务的功能。除了连续性以外,创建协程还涉及到作用于 CoroutineScope 内的 Job(具有生命周期的可取消工作单元)的内容。CoroutineScope 表示以递归方式对其子级以及这些子级的子级强制执行取消和其他规则的一种上下文。Dispatcher 会管理协程将使用哪个后备线程来执行任务,从而使开发者无需管理使用新线程的时间和位置。

概念解释
Job表示可取消的工作单元,例如使用 launch() 函数创建的工作单元。
CoroutineScope用于创建新协程的函数,例如 launch()async()CoroutineScope 进行扩展。
Dispatcher调度程序确定协程将使用的线程。Main 调度程序将始终在主线程上运行协程,而 DefaultIOUnconfined 等调度程序则会使用其他线程。

三、协程

  • GlobalScope.launch

    只要应用在运行,GlobalScope 便允许其中的任何协程运行。鉴于我们讨论的关于主线程的原因,我们不建议在示例代码之外使用这种方法。在应用中使用协程时,我们会使用其他作用域。

    launch() 函数会根据括起来的代码(封装在可取消作业对象中)创建协程。launch() 用于无需在协程范围之外返回值的情况。

    我们来看一下 launch() 的完整签名,以了解协程中的下一个重要概念。

    fun CoroutineScope.launch {
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> Unit
    }
    

    在后台,您传递给 launch 函数的代码块会使用 suspend 关键字进行标记。Suspend 表示代码块或函数可以暂停或恢复。

  • runBlocking 简述

    该函数会启动新协程并在新协程完成之前阻塞当前线程。它主要用于在主要函数和测试中的阻塞代码和非阻塞代码之间架起桥梁。该函数在典型的 Android 代码中并不常用。

    import kotlinx.coroutines.*
    import java.time.LocalDateTime
    import java.time.format.DateTimeFormatter
    
    val formatter = DateTimeFormatter.ISO_LOCAL_TIME
    val time = { formatter.format(LocalDateTime.now()) }
    
    suspend fun getValue(): Double {
        println("entering getValue() at ${time()}")
        delay(3000)
        println("leaving getValue() at ${time()}")
        return Math.random()
    }
    
    fun main() {
        runBlocking {
            val num1 = getValue()
            val num2 = getValue()
            println("result of num1 + num2 is ${num1 + num2}")
        }
    }
    
  • async()

    上述的函数会字啊第一个getValue()执行完毕再执行另外一个。而使用async()修饰的函数则会异步操作,在第一个函数等待期间执行其它函数

    fun main() {
        runBlocking {
            val num1 = async { getValue() }
            val num2 = async { getValue() }
            println("result of num1 + num2 is ${num1.await() + num2.await()}")
        }
    }
    
    Fun CoroutineScope.async() {
        context: CoroutineContext = EmptyCoroutineContext,
        start: CoroutineStart = CoroutineStart.DEFAULT,
        block: suspend CoroutineScope.() -> T
    }: Deferred<T>
    

    async() 函数会返回 Deferred 类型的值。Deferred 是一个可取消的 Job,可以存储对未来值的引用。使用 Deferred,您仍然可以调用函数,就像它会立即返回一个值一样 - Deferred 只充当占位符,因为您无法确定异步任务将何时返回。Deferred(在其他语言中也称为 promise 或 future)能够保证稍后会向此对象返回一个值。另一方面,异步任务默认不会阻塞或等待执行。若要启动异步任务,当前的代码行需要等待 Deferred 的输出,您可以对其调用 await()。它将会返回原始值

  • 何时将函数标记为 suspend

    在前面的示例中,您可能已经注意到 getValue() 函数也使用 suspend 关键字进行了定义。原因在于它会调用 delay(),这也是一个 suspend 函数。只要一个函数调用另一个 suspend 函数,那它也应是 suspend 函数。

    如果这样的话,为什么我们示例中的 main() 函数不能用 suspend 进行标记呢?毕竟,它也调用 getValue()

    不一定。getValue() 实际上是在传递给 runBlocking() 的函数中调用的,这是一个 suspend 函数,类似于传递给 launch()async() 的函数。而 getValue() 不是在 main() 本身中调用的,runBlocking() 也不是 suspend 函数,因此 main() 没有用 suspend 标记。如果函数未调用 suspend 函数,它本身就无需是 suspend 函数。

四、线程的切换

协程是在线程上创建的,因此如果协程创建在主线程上面的话,执行网络请求的话依然会使程序出现错误。所以类似于下文的代码是不正确的

viewModelScope.launch() {
    val jsonBody = "{ username: \"$username\", token: \"$token\"}"
    loginRepository.makeLoginRequest(jsonBody)//执行网络请求
}

因此需要使用Dispatcher调度程序将线程切换到子线程,如下:

viewModelScope.launch(Dispatchers.IO) {//如果不写这个默认就是挂在主线程上面,程序还是会出现异常
    val jsonBody = "{ username: \"$username\", token: \"$token\"}"
    loginRepository.makeLoginRequest(jsonBody)
}

或者withContext(Dispatchers.IO)使用``将makeLoginRequest改为子线程,如下:

withContext(Dispatchers.IO) {
            // Blocking network request code
        }

withContext(Dispatchers.IO)中的代码执行完毕后,程序自动回到原先的线程而无需切换。同时使用withContext(Dispatchers.IO)的函数需要使用挂起suspend来进行修饰。

完整代码如下:

sealed class Result<out R> {
    data class Success<out T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
}
private const val loginUrl = "https://www.baidu.com/"
class LoginRepository(private val responseParser: LoginResponseParser) {

    // Function that makes the network request, blocking the current thread
    suspend fun makeLoginRequest(
        jsonBody: String
    ): Result<LoginResponse> {
        val url = URL(loginUrl)
        (url.openConnection() as? HttpURLConnection)?.run {
            requestMethod = "POST"
            setRequestProperty("Content-Type", "application/json; utf-8")
            setRequestProperty("Accept", "application/json")
            doOutput = true
            outputStream.write(jsonBody.toByteArray())
            return Result.Success(responseParser.parse(inputStream))
        }
        return Result.Error(Exception("Cannot open HttpURLConnection"))
    }
}

五、参考链接

  1. 协程简介

    https://developer.android.google.cn/codelabs/basic-android-kotlin-training-introduction-coroutines

  2. Kotlin协程

    https://developer.android.google.cn/kotlin/coroutines

  3. 协程

    https://kotlinlang.org/docs/coroutines-guide.html

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-07-28 07:56:17  更:2021-07-28 07:58:04 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/3 7:36:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码