首页 > 其他分享 >Android协程带你飞越传统异步枷锁

Android协程带你飞越传统异步枷锁

时间:2023-08-08 11:36:45浏览次数:31  
标签:异步 协程 val Coroutine 线程 Resource Android

引言

在Android开发中,处理异步任务一直是一项挑战。以往的回调和线程管理方式复杂繁琐,使得代码难以维护和阅读。Jetpack引入的Coroutine(协程)成为了异步编程的新标杆。本文将深入探讨Android Jetpack Coroutine的使用、原理以及高级用法,助您在异步编程的路上游刃有余。

什么是Coroutine?

Coroutine是一种轻量级的并发设计模式,它允许开发者以顺序代码的方式处理异步任务,避免了传统回调和线程管理带来的复杂性。它建立在Kotlin语言的suspend函数上,suspend函数标记的方法能够挂起当前协程的执行,并在异步任务完成后恢复执行。

Coroutine的优势

  • 简洁:通过简洁的代码表达异步逻辑,避免回调地狱。
  • 可读性:顺序的代码结构使得逻辑更加清晰易懂。
  • 卓越的性能:Coroutine能够有效地利用线程,避免过度的线程切换。
  • 取消支持:通过Coroutine的结构,方便地支持任务取消和资源回收。
  • 适用范围广:从简单的后台任务到复杂的并发操作,Coroutine都能应对自如。

Coroutine的原理

挂起与恢复

当遇到挂起函数时,例如delay()或者进行网络请求的suspend函数,协程会将当前状态保存下来,包括局部变量、指令指针等信息,并暂停协程的执行。然后,协程会立即返回给调用者,释放所占用的线程资源。一旦挂起函数的异步操作完成,协程会根据之前保存的状态恢复执行,就好像从挂起的地方继续运行一样,这使得异步编程变得自然、优雅。

线程调度与切换

Coroutine使用调度器(Dispatcher)来管理协程的执行线程。主要的调度器有:

  • Dispatchers.Main:在Android中主线程上执行,用于UI操作。
  • Dispatchers.IO:在IO密集型任务中使用,比如网络请求、文件读写。
  • Dispatchers.Default:在CPU密集型任务中使用,比如复杂的计算。

线程切换通过withContext()函数实现,它智能地在不同的调度器之间切换,避免不必要的线程切换开销,提高性能。

异常处理与取消支持

Coroutine支持异常处理,我们可以在协程内部使用try-catch块来捕获异常,并将异常传播到协程的外部作用域进行处理,这使得我们能够更好地管理和处理异步操作中出现的异常情况。

同时,Coroutine支持任务的取消。当我们不再需要某个协程执行时,可以使用coroutineContext.cancel()或者coroutinecope.cancel()来取消该协程。这样,协程会自动释放资源,避免造成内存泄漏。

基本用法

并发与并行

使用async函数,我们可以实现并发操作,同时执行多个异步任务,并等待它们的结果。而使用launch函数,则可以实现并行操作,多个协程在不同线程上同时执行。

val deferredResult1 = async { performTask1() }
val deferredResult2 = async { performTask2() }

val result1 = deferredResult1.await()
val result2 = deferredResult2.await()

超时与异常处理

通过withTimeout()函数,我们可以设置一个任务的超时时间,当任务执行时间超过指定时间时,会抛出TimeoutCancellationException异常。这使得我们能够灵活地处理超时情况。

try {
    withTimeout(5000) {
        performLongRunningTask()
    }
} catch (e: TimeoutCancellationException) {
    // 处理超时情况
}

组合挂起函数

Coroutine提供了一系列的挂起函数,例如delay()withContext()等。我们可以通过asyncawait()函数将这些挂起函数组合在一起,实现复杂的异步操作。

val result1 = async { performTask1() }.await()
val result2 = async { performTask2() }.await()

与jetpack联动

当使用Jetpack组件和Coroutine结合起来时,我们可以在Android应用中更加优雅地处理异步任务。下面通过一个示例演示如何在ViewModel中使用Jetpack组件和Coroutine来处理异步数据加载:

创建一个ViewModel类,例如MyViewModel.kt,并在其中使用Coroutine来加载数据:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.liveData
import kotlinx.coroutine.Dispatchers

class MyViewModel : ViewModel() {

    fun loadData() = liveData(Dispatchers.IO) {
        emit(Resource.Loading) // 发送加载中状态

        try {
            // 模拟耗时操作
            val data = fetchDataFromRemote()
            emit(Resource.Success(data)) // 发送加载成功状态
        } catch (e: Exception) {
            emit(Resource.Error(e.message)) // 发送加载失败状态
        }
    }

    // 假设这是一个网络请求的方法
    private suspend fun fetchDataFromRemote(): String {
        // 模拟耗时操作
        delay(2000)
        return "Data from remote"
    }
}

创建一个Resource类用于封装数据状态:

sealed class Resource<out T> {
    object Loading : Resource<Nothing>()
    data class Success<T>(val data: T) : Resource<T>()
    data class Error(val message: String?) : Resource<Nothing>()
}

在Activity或Fragment中使用ViewModel,并观察数据变化:

class MyActivity : AppCompatActivity() {

    private val viewModel: MyViewModel by viewModels()

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

        viewModel.loadData().observe(this) { resource ->
            when (resource) {
                is Resource.Loading -> {
                    // 显示加载中UI
                }
                is Resource.Success -> {
                    // 显示加载成功UI,并使用resource.data来更新UI
                    val data = resource.data
                }
                is Resource.Error -> {
                    // 显示加载失败UI,并使用resource.message显示错误信息
                    val errorMessage = resource.message
                }
            }
        }
    }
}

在以上示例中,ViewModel中的loadData()方法使用Coroutine的liveData构建器来执行异步任务。我们通过emit()函数发送不同的数据状态,Activity(或Fragment)通过观察LiveData来处理不同的状态,并相应地更新UI。

结论

Android Jetpack Coroutine是异步编程的高级艺术。通过深入理解Coroutine的原理和高级用法,我们可以写出更加优雅、高效的异步代码。掌握Coroutine的挂起与恢复、线程切换、异常处理和取消支持,使得我们能够更好地处理异步操作,为用户带来更出色的应用体验。

标签:异步,协程,val,Coroutine,线程,Resource,Android
From: https://blog.51cto.com/u_16175630/7006195

相关文章

  • c# 同步 异步 (转)
    技巧c#Task返回值Task返回值,目前有2种情况,一种是异步async返回值,一种是同步返回值第一种:异步返回值Task方法如果加了async关键字,那么就是异步返回方法,如果是异步返回方法,需要返回一个值时,直接returnvalue,就可以了。第二种:同步返回值Task方法如果没有加async关键字,需要返回......
  • Android平台一对一音视频通话方案对比:WebRTC VS RTMP VS RTSP
    一对一音视频通话使用场景一对一音视频通话都需要稳定、清晰和流畅,以确保良好的用户体验,常用的使用场景如下:社交应用:社交应用是一种常见的使用场景,用户可以通过音视频通话进行面对面的交流;在线教育:老师和学生可以通过音视频通话功能进行实时互动,提高教学效率;远程协助:在某些工作场景......
  • UART异步通信配置步骤/HAL
    1串口工作参数MX_USART2_UART_Init2底层初始化HAL_UART_MspInit3开启串口异步接收中断HAL_UART_Receive_IT4设置优先级,使能中断HAL_NVIC_SetPriority / HAL_NVIC_SetPriority5编写中断服务函数USART2_IRQHandler/HAL_UART_IRQHandler/HAL_UART_R......
  • Android 平台架构
    放一张官网的图各层级描述参考官网:https://developer.android.google.cn/guide/platform?hl=zh-cnHAL层出现的原因:......
  • Android 系统 映像文件
    通过Android拉取ASOP项目源码,配好各种环境后,编译出来的文件,有如下几个:通过Android拉取ASOP项目源码,配好各种环境后,编译出来的文件,有如下几个:boot.img包含内核启动参数、内核等多个元素ramdisk.img小型的文件系统,是Android系统启动的关键system.imgAndroid系统......
  • 【金九银十面试冲刺】Android岗面试题每日分享——Java篇
    一、Java异常机制中,异常Exception与错误Error区别这道题想考察什么?在开发时需要时候需要自定义异常时,应该选择定义Excption还是Error?编写的代码触发Excption或者Error分别代表什么?考察的知识点Java异常机制考生应该如何回答在Java中存在一个Throwable可抛出类,Throwable有两个重要的......
  • Android View动态设置有圆角的背景颜色
    valshapeDrawable=GradientDrawable()//设置形状为矩形shapeDrawable.shape=GradientDrawable.RECTANGLE//设置背景颜色shapeDrawable.setColor(Color.parseColor(item.bgColour))//创建一个圆角数组,分别表示左上......
  • 荣耀90pro无法在Android Studio中使用USB真机调试问题
    今日用荣耀手机进行真机调试,由于之前没有用过华为荣耀手机,所以在连接过程中出现了一些问题。首先,我还是像以前那样用数据线将手机和电脑连接,在手机开发者模式中开启USB调试,结果发现NoDevices。 之后发现,在打开USB调试时,提示如下信息,这跟我之前用其他手机提示的不一样,我记得是......
  • Android模拟器DNS设置、使用adb命令获取手机ip地址
    https://blog.csdn.net/bonardgalton/article/details/5353296Android模拟器默认的地址是10.0.2.3,默认的DNS也是10.0.2.3,对于在家里上网学习Android的人(像我)来讲,一般电脑的IP都是192.168.1.100之类的,不在同一个网段。所以就会出现电脑可以上网但是模拟器不能上网的情况。其实设置......
  • Android Activity的创建流程(Android-10)
    前言本篇笔记从最基础的startActivity(Intent)看一下Activity的启动流程。同时由于Launcher的启动后续和这里基本类似,就记录在一起。客户端发送请求startActivity发送启动请求的流程本质上就是:向ActivityManagerService发送启动请求。由于发送端基本是在当前用户App进程或者Launche......