Android Kotlin 协程async

文章详细介绍了Kotlin协程中async函数的使用,包括如何通过measureTimeMillis统计代码执行时间,如何通过async实现并发执行以提高效率,以及惰性启动的async和结构化并发的概念。此外,还提到了异常处理和协程在Android中的应用,以及协程的原理和实战应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android Kotlin 协程async

在这里插入图片描述

measureTimeMillis 统计一段代码耗时

内敛函数 measureTimeMillis{ } 可以很方便的统计一段代码执行的耗时。

使用:

GlobalScope.launch {
    val time = measureTimeMillis {
        delay(1000)
        Log.d("zyj-", "日志")
    }
    Log.d("zyj-", "耗时:$time")
}

输出结果:

D/zyj-: 日志
D/zyj-: 耗时:1010

使用默认顺序

定义两个耗时函数:

suspend fun doSomethingUsefulOne(): Int {
    delay(1000L) // 假设我们在这里做了一些有用的事
    return 13
}

suspend fun doSomethingUsefulTwo(): Int {
    delay(1000L) // 假设我们在这里也做了一些有用的事
    return 29
}

使用默认的顺序调用:

val time = measureTimeMillis {
    val one = doSomethingUsefulOne()
    val two = doSomethingUsefulTwo()
    println("The answer is ${one + two}")
}
println("Completed in $time ms")

它的打印输出如下:

The answer is 42
Completed in 2017 ms

从输出结果上看,两个耗时任务是串行的,总耗时= 耗时函数1 + 耗时函数2

使用 async 并发

如果 doSomethingUsefulOne 与 doSomethingUsefulTwo 之间没有依赖,并且我们想更快的得到结果,让它们进行 并发 吗?这就是async 可以帮助我们的地方。

在概念上,async 就类似于 launch。它启动了一个单独的协程与其它所有的协程一起并发的工作。不同之处在于 launch 返回一个 Job 并且不附带任何结果值,而 async 返回一个 Deferred—— 一个非阻塞 future, 这代表了一个将会在稍后提供结果的 promise。你可以使用 .await()在一个延期的值上得到它的最终结果, 但是 Deferred 也是一个 Job,所以如果需要的话,你可以取消它。

val time = measureTimeMillis {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")

它的打印输出如下:

The answer is 42
Completed in 1017 ms

这里快了两倍,因为两个协程并发执行。 请注意,使用协程进行并发总是显式的。

惰性启动的 async

可选的,async 可以通过将 start 参数设置为 CoroutineStart.LAZY 而变为惰性的。 在这个模式下,只有结果通过 await 获取的时候协程才会启动,或者在 Job 的 start函数调用的时候。运行下面的示例:

val time = measureTimeMillis {
    val one = async(start = CoroutineStart.LAZY) { doSomethingUsefulOne() }
    val two = async(start = CoroutineStart.LAZY) { doSomethingUsefulTwo() }
    // 执行一些计算
    one.start() // 启动第一个
    two.start() // 启动第二个
    println("The answer is ${one.await() + two.await()}")
}
println("Completed in $time ms")

它的打印输出如下:

The answer is 42
Completed in 1017 ms

因此,在先前的例子中这里定义的两个协程没有执行,但是控制权在于程序员准确的在开始执行时调用 start。我们首先 调用 one,然后调用 two,接下来等待这个协程执行完毕。

注意,如果我们只是在 println 中调用 await,而没有在单独的协程中调用 start,这将会导致顺序行为,直到 await 启动该协程 执行并等待至它结束,这并不是惰性的预期用例。 在计算一个值涉及挂起函数时,这个 async(start = CoroutineStart.LAZY)的用例用于替代标准库中的 lazy 函数。

构建async 风格的函数

我们可以定义异步风格的函数来 异步 的调用 doSomethingUsefulOne 和 doSomethingUsefulTwo 并使用 async 协程建造器并带有一个显式的 GlobalScope 引用。 我们给这样的函数的名称中加上“……Async”后缀来突出表明:事实上,它们只做异步计算并且需要使用延期的值来获得结果。

// somethingUsefulOneAsync 函数的返回值类型是 Deferred<Int>
fun somethingUsefulOneAsync() = GlobalScope.async {
    doSomethingUsefulOne()
}

// somethingUsefulTwoAsync 函数的返回值类型是 Deferred<Int>
fun somethingUsefulTwoAsync() = GlobalScope.async {
    doSomethingUsefulTwo()
}

使用

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // 我们可以在协程外面启动异步执行
        val one = somethingUsefulOneAsync()
        val two = somethingUsefulTwoAsync()

        // 但是等待结果必须调用其它的挂起或者阻塞
        // 当我们等待结果的时候,这里我们使用 `GlobalScope.launch{ }` 来阻塞主线程
        GlobalScope.launch {
            println("The- answer is ${one.await()}  ${two.await()}")
        }
    }
}

这种带有异步函数的编程风格仅供参考,因为这在其它编程语言中是一种受欢迎的风格。在 Kotlin 的协程中使用这种风格是强烈不推荐的, 原因如下所述。

考虑一下如果 val one = somethingUsefulOneAsync() 这一行和 one.await() 表达式这里在代码中有逻辑错误, 并且程序抛出了异常以及程序在操作的过程中中止,将会发生什么。 通常情况下,一个全局的异常处理者会捕获这个异常,将异常打印成日记并报告给开发者,但是反之该程序将会继续执行其它操作。但是这里我们的 somethingUsefulOneAsync仍然在后台执行, 尽管如此,启动它的那次操作也会被终止。这个程序将不会进行结构化并发,如下一小节所示。

使用 async 的结构化并发

让我们使用使用 async 的并发这一小节的例子并且提取出一个函数并发的调用 doSomethingUsefulOne 与 doSomethingUsefulTwo 并且返回它们两个的结果之和。 由于 async 被定义为了 CoroutineScope上的扩展,我们需要将它写在作用域内,并且这是 coroutineScope 函数所提供的:

suspend fun concurrentSum(): Int = coroutineScope {
    val one = async { doSomethingUsefulOne() }
    val two = async { doSomethingUsefulTwo() }
    one.await() + two.await()
}

这种情况下,如果在 concurrentSum 函数内部发生了错误,并且它抛出了一个异常, 所有在作用域中启动的协程都会被取消。

val time = measureTimeMillis {
    println("The answer is ${concurrentSum()}")
}
println("Completed in $time ms")

从上面的 main 函数的输出可以看出,我们仍然可以同时执行这两个操作:

The answer is 42
Completed in 1017 ms

取消始终通过协程的层次结构来进行传递:

import kotlinx.coroutines.*

fun main() = runBlocking<Unit> {
    try {
        failedConcurrentSum()
    } catch(e: ArithmeticException) {
        println("Computation failed with ArithmeticException")
    }
}

suspend fun failedConcurrentSum(): Int = coroutineScope {
    val one = async<Int> { 
        try {
            delay(Long.MAX_VALUE) // 模拟一个长时间的运算
            42
        } finally {
            println("First child was cancelled")
        }
    }
    val two = async<Int> { 
        println("Second child throws an exception")
        throw ArithmeticException()
    }
    one.await() + two.await()
}

请注意,如果其中一个子协程(即 two)失败,第一个 async 以及等待中的父协程都会被取消:

Second child throws an exception
First child was cancelled
Computation failed with ArithmeticException

后文

Android Kotlin 更多学习资料可以扫码免费领取!

史上最详Android版kotlin协程入门进阶实战

第一章 Kotlin协程的基础介绍

​ ● 协程是什么

​ ● 什么是Job 、Deferred 、协程作用域

​ ● Kotlin协程的基础用法

img

第二章 kotlin协程的关键知识点初步讲解

​ ● 协程调度器

​ ● 协程上下文

​ ● 协程启动模式

​ ● 协程作用域

​ ● 挂起函数

img

第三章 kotlin协程的异常处理

​ ● 协程异常的产生流程

​ ● 协程的异常处理

img

第四章 kotlin协程在Android中的基础应用

​ ● Android使用kotlin协程

​ ● 在Activity与Framgent中使用协程

​ ● ViewModel中使用协程

​ ● 其他环境下使用协程

img

第五章 kotlin协程的网络请求封装

​ ● 协程的常用环境

​ ● 协程在网络请求下的封装及使用

​ ● 高阶函数方式

​ ● 多状态函数返回值方式

img

第六章 深入kotlin协程原理(一)

​ ● suspend的花花肠子

​ ● 藏在身后的-Continuation

​ ● 村里的希望-SuspendLambda

img

第七章 深入kotlin协程原理(二)

​ ● 协程的那些小秘密

​ ● 协程的创建过程

​ ● 协程的挂起与恢复

​ ● 协程的执行与状态机

img

第八章 Kotlin Jetpack 实战

​ ● 从一个膜拜大神的 Demo 开始

​ ● Kotlin 写 Gradle 脚本是一种什么体验?

​ ● Kotlin 编程的三重境界

​ ● Kotlin 高阶函数

​ ● Kotlin 泛型

​ ● Kotlin 扩展

​ ● Kotlin 委托

​ ● 协程“不为人知”的调试技巧

​ ● 图解协程原理

img

第九章 Kotlin + 协程 + Retrofit + MVVM优雅的实现网络请求

​ ● 项目配置

​ ● 实现思路

​ ● 协程实现

​ ● 协程 + ViewModel + LiveData实现

​ ● 后续优化

​ ● 异常处理

​ ● 更新Retrofit 2.6.0

img

由于文章内容比较多,篇幅有限,资料已经被整理成了PDF文档,有需要 Android中高级面试必知必会 完整文档的可以加微信可免费领取!(承诺:100%免费)

更有更多资料,加微信免费领取

img

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值