android 协程基础(一)

背景:上家公司干了三年,开发语言主要用的JAVA和flutter来开发的,新的公司全是kotlin,所以又要把上上家公司用的kotlin要回顾下了,主要还是协程和Jetpack相关。

基础概念

协程

协程并不是一个新的概念,它并不是 Kotlin 发明的。它们已经存在了几十年,并且在 Go 等其他一些编程语言中很受欢迎。

官方的描述是:协程通过将复杂性放入库来简化异步编程。程序的逻辑可以在协程中顺序地表达,而底层库会为我们解决其异步性。该库可以将用户代码的相关部分包装为回调、订阅相关事件、在不同线程(甚至不同机器)上调度执行,而代码则保持如同顺序执行一样简单。

对比其他几种异步编程的方式:线程、回调、Rxjava:

  • 线程 线程的确很好用,也方便用线程池管理,不过线程的上下文切换是消耗性能的,不过这是可以优化的,可以使用CAS或者优化锁抢占等来优化。
  • 回调 一个函数作为参数传递给另一个函数,在处理完后回调用此函数。确实比较好用,但是业务比较复杂,要小心嵌套陷阱和阻塞。
  • Rxjava 这类响应式编程确实很方便,也不存在多层级嵌套。一切皆是流,并且它还是可观察的。一种比较完美的解决方案,学习成本也比较高。

协程:是一种可以挂起和恢复的编程模型,可以让我们异步逻辑同步化,决绝回调地狱。看个小例子:

fun postBean(bean: Bean) {
    launch {
        val token = preparePost()
        val post = submitPost(token, bean)
        processPost(post)
    }
}

suspend fun preparePost(): Token {
    // 发起请求并挂起该协程
    return suspendCoroutine { /* ... */ } 
}

preparePost函数是个挂起函数,是异步的,但是代码逻辑确是同步化的。

挂起和恢复

协程的核心点就是,函数或者一段程序能够挂起,稍后在挂起的位置恢复
协程添加了suspendresume关键字来操作:

  • suspend 挂起 ,用于暂停执行当前协程,并保存所有局部变量
  • resume 用于让暂停的协程从挂起处继续执行

需要注意的是:挂起函数(被suspend修饰的)只能在协程体内或其他挂起函数内调用。

挂起和阻塞

  • 相同点 都是异步操作
  • 区别 挂起是不会占用CPU的,但是会保存状态,在某个时间恢复执行。而阻塞一般是进程等待资源是发生,会占用系统资源。

调度器

所有的协程必须在调度器中运行,即使在主线程也是一样。

Dispatchers.Main

Android上的主线程,用于处理UI交互和一些轻量级任务,eg:调用UI函数、调用suspend函数、更新LiveData。

Dispatchers.IO

非主线程,专为磁盘和网络IO进行了优化,eg:数据库、文件读写、网络请求。

Dispatchers.Default

非主线程,协程开启的默认调度器,专为CPU密集型任务进行了优化,eg:数组排序、JSON解析、复杂逻辑处理、计算处理。

eg:一个简单的调度器上下文切换代码:

 GlobalScope.launch(Dispatchers.IO){
 			//getUser() 是个挂起耗时函数 
            val user = getUser()
            withContext(Dispatchers.Main){
            	//主线程更新UI
                binding.testText.text = user.name
            }
        }

newSingleThreadContext

这种调度会为协程启动一个单独的线程,线程名为newSingleThreadContext的参数。一个专用的线程是一种消耗的资源,需要在程序中释放,调用其close方法。或者应该在程序的顶层被复用。

val newSingleThreadContext = newSingleThreadContext("single")
        GlobalScope.launch(newSingleThreadContext) {
            println("newSingleThreadContext -> thread: ${Thread.currentThread().name}")
        }
        newSingleThreadContext.close()

结构化并发-CoroutineScope

一个场景:当某个协程任务丢失,无法追踪,会导致内存、CPU等资源浪费,也行会发送一个网络请求或者缓存读取任务,但是没有接受,这种场景称为任务泄漏。
所以kotlin又加入了结构化并发,跟之前学过的Rxjava定义生命周期也是一样的,其实就是作用域的限定和追踪。

定义协程必须指定起CoroutineScope,它会追踪所有协程,同样它还可以取消它所启动的协程。

public interface CoroutineScope {
    /**
     * The context of this scope.
     * Context is encapsulated by the scope and used for implementation of coroutine builders that are extensions on the scope.
     * Accessing this property in general code is not recommended for any purposes except accessing the [Job] instance for advanced usages.
     *
     * By convention, should contain an instance of a [job][Job] to enforce structured concurrency.
     */
    public val coroutineContext: CoroutineContext
}

官方解释:这个范围的上下文。上下文由范围封装,并用于作为范围扩展的协程构建器的实现。除了在高级用法中访问Job实例外,不建议出于任何目的访问通用代码中的此属性。
按照约定,应该包含一个作业实例来强制结构化并发。

定义新协程的作用域。每个协程构建器(比如launch,
async等)都是CoroutineScope的扩展,并继承它的coroutineContext来自动传播它的所有元素和取消。

获得独立作用域实例的最佳方法是CoroutineScope()和MainScope()工厂函数。可以使用加号操作符将其他上下文元素附加到作用域。
结构化并发规范:
不建议手动实现此接口,建议采用委托实现。按照约定,作用域的上下文应该包含一个作业实例,以通过取消的传播来强制结构化并发原则。

每个协程构建器(如launch, async等)和每个作用域函数(如coroutineScope,
withContext等)都提供了自己的作用域,并将自己的Job实例放入它运行的代码内部块中。按照惯例,它们都要等待它们块中的所有协程完成,然后才能完成它们自己,从而实现结构化并发。
Android使用: Android在所有具有生命周期的实体中都支持协程作用域。

相关API:

  • GlobalScope 生命周期是进程级别的,即使activity和fragment已经销毁,协程依然执行
 GlobalScope.launch {

        }
  • MainScope 在Activity中使用,可以在onDestory(). 中取消协程
 MainScope().launch {

        }
  • viewModelScope 只能在ViewModel中使用,绑定ViewModel的生命周期
//implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1")
 viewModelScope.launch {

        }
  • lifecycleScope 只能在Activity、Fragment中使用,会绑定Activity和Fragment的生命周期
 lifecycleScope.launch {

        }

调试

为了方便打印协程名字等信息。可以添加调试打印。

在这里插入图片描述
添加-Dkotlinx.coroutines.debug=on
在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值