Android体系课之--Kotlin协程入门篇-协程的基本使用

简介: 笔者在写这篇文章之前,也白嫖了很多关于Kotlin协程的文章: 这里笔者将他们分为三种:- 1.讲的内容很*浅*,没几句可能就结束了,看完就索然无味了- 2.讲的内容很*深*,看到一半就开始晕乎乎了,然后可能还是手机好玩。。- 3.内容比较*适中*,读者可以在里面获取到一些协程的基本信息,关键内容可能就浅尝辄止了,很难获取到核心知识点

前言:

笔者在写这篇文章之前,也白嫖了很多关于Kotlin协程的文章: 这里笔者将他们分为三种:

  • 1.讲的内容很,没几句可能就结束了,看完就索然无味了
  • 2.讲的内容很,看到一半就开始晕乎乎了,然后可能还是手机好玩。。
  • 3.内容比较适中,读者可以在里面获取到一些协程的基本信息,关键内容可能就浅尝辄止了,很难获取到核心知识点
知识的学习就像谈恋爱,不能一上来就想和对方深入了解,也不能聊得太浅,影响后续发展,得讲究循序渐进。 接下来笔者会根据由浅入深的方式,阶段性来讲解Kotlin协程相关知识点。读者可以根据自己的需求,选择对应的阶段文章。

笔者主要以下面几个时间线来讲解协程

Android体系课之--Kotlin协程入门篇-协程的基本使用(一)

.Android体系课之--Kotlin协程进阶篇-协程中关键知识点梳理(二)

Android体系课之--Kotlin协程进阶篇-协程的异常处理机制以及suspend关键字详解

Android体系课之--Kotlin协程进阶篇-协程加Retrofit创建一个MVVM模式的网络请求框架(四)

本系列文使用的版本

// Kotlin
implementation "org.jetbrains.kotlin:kotlin-stdlib:1.4.32"
// 协程核心库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.4.1"
// 协程Android支持库
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.4.1"

简介:

本文主要讲解的是协程的基本用法。 在将基础用法前,我们先来了解下什么是协程

协程到底是什么?

我们这里引用官方的定义

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

2.协程是一种并发设计模式,您可以在Android平台上使用它来简化异步执行的代码

我们使用GlobalScope来创建一个协程:

Job job = GlobalScope.launch { 
    println(this)
}

这里有几个元素: GlobalScope:看名字就知道这个协程的是一个针对全局的的顶级协程 Job:理解为一个任务,和线程类似,有自己的生命周期,Job中可以有子Job 。

Job状态

State [isActive] [isCompleted] [isCancelled]
New (optional initial state) false false false
Active (default initial state) true false false
Completing (transient state) true false false
Cancelling (transient state) false false true
Cancelled (final state) false true true
Completed (final state) false true false

协程生命周期?

我们引用官方的图来看下协程的的生命周期过程:


                                      wait children
+-----+ start  +--------+ complete   +-------------+  finish  +-----------+
| New | -----> | Active | ---------> | Completing  | -------> | Completed |
+-----+        +--------+            +-------------+          +-----------+
                 |  cancel / fail       |
                 |     +----------------+
                 |     |
                 V     V
             +------------+                           finish  +-----------+
             | Cancelling | --------------------------------> | Cancelled |
             +------------+                                   +-----------+

协程的几种创建方式runBlocking:阻塞创建协程的线程,并运行协程作用域中的job任务 launch:不阻塞创建线程,返回一个Job,且协程没有被挂起,任务执行中可以被异步取消 async:不阻塞创建线程,且协程没有被挂起,返回一个DeferredCoroutine,此时协程状态为Active,

这里们有讲到协程作用域,下面我们来分析下他是个什么东西 协程作用域?

协程作用域是协程的运行作用范围 来看下下面的例子: var job = GloalScope.launch { val test = 1; println(this.isActive) } println("$test") 这个例子编译会报Unresolved reference: test,说明test是在另外一个作用域中。和我们使用{}括起来的作用域一样

协程作用域分类?

顶级作用域,协同作用域和主从作用域,后续会详细讲解

协程的基本使用方式?

我们使用runBlockinglaunchasync分别来启动对应协程。


fun coroutline(){
    val run = runBlocking {
        println("runBlocking:启动一个协程")
        123
    }
    println("runBlocking:return:$run")
    val la = GlobalScope.launch {
        println("launch:启动一个协程")
        345
    }
    println("GlobalScope.launch:return:$la")
    val async = GlobalScope.async {
        println("async:启动一个协程")
        567
    }
    println("GlobalScope.async:return:$async")
}
运行结果如下:
runBlocking:启动一个协程
runBlocking:return:123
launch:启动一个协程
GlobalScope.launch:return:"coroutine#2":StandaloneCoroutine{Active}@34ce8af7
GlobalScope.async:return:"coroutine#3":DeferredCoroutine{Active}@511baa65
async:启动一个协程
也可能是下面这个:
runBlocking:启动一个协程
runBlocking:return:123
GlobalScope.launch:return:"coroutine#2":StandaloneCoroutine{Active}@34ce8af7
launch:启动一个协程
GlobalScope.async:return:"coroutine#3":DeferredCoroutine{Active}@511baa65
async:启动一个协程
又可能是下面这个
runBlocking:启动一个协程
runBlocking:return:123
GlobalScope.launch:return:"coroutine#2":StandaloneCoroutine{Active}@34ce8af7
launch:启动一个协程
async:启动一个协程
GlobalScope.async:return:"coroutine#3":DeferredCoroutine{Active}@511baa65

但是不管怎么变:第一个runBlocking顺序始终是先执行协程作用域中的任务,再继续执行原线程的任务,因为此时主线程是阻塞的 而launch和async因为是非阻塞的,所以原线程和协程作用域线程是异步执行的,打印日志也是无序的

我们来看下几种创建协程方式对于的返回值? runBlocking:

返回了123,是协程作用域中最后一行代码,这个有点类似java中线程的写法:

int x;
main
{
    boolean isGo = false;
    new Thread(){
        void run(){
            ...
            ...
            x = 123;
            isGo = true;
        }
    }
    while(!isGo){
        Thread.sleep(100);
    }
}

是不是感觉你也写过这种代码:是的在Kotlin中我们只需要一行代码就可以解决

launch方法创建的协程

返回值是一个

StandaloneCoroutine:"coroutine#2":StandaloneCoroutine{Active}@34ce8af7
这个job可以使我们在异步操作中对协程取消。未完成的协程如果不取消,则会出现内存溢出的情况

async方法创建的协程

返回值是也是一个DeferredCoroutine:这个和launch创建的协程返回值有啥区别呢 我们看DeferredCoroutine定义:


public fun <T> CoroutineScope.async(
 context: CoroutineContext = EmptyCoroutineContext,
 start: CoroutineStart = CoroutineStart.DEFAULT,
 block: suspend CoroutineScope.() -> T
): Deferred<T> {
 val newContext = newCoroutineContext(context)
 val coroutine = if (start.isLazy)
 LazyDeferredCoroutine(newContext, block) else
 DeferredCoroutine<T>(newContext, active = true)
 coroutine.start(start, coroutine, block)
 return coroutine
}

可以看到返回的是一个Deferred对象

public interface Deferred<out T> : Job {
    public suspend fun await(): T
    ...
}

Deferred内部有个await方法 这个方法注释:等待一个协程作用域执行完成,并返回最后一行值,或者协程被取消抛出一个异常

我们使用async来创建一个协程:


GlobalScope.launch {
    val async = GlobalScope.async {
        println("async:启动一个协程")
        Thread.sleep(2000)
        567
    }
    println("GlobalScope.async:return:${async.await()}")
    println("GlobalScope.async:return:after")
}
打印结果:
async:启动一个协程
GlobalScope.async:return:567
GlobalScope.async:return:after

看到在调用async.await后,这个时候主线程会阻塞,等待2s协程执行完毕后,才打印出返回值“567”

上面我们允许的async.await操作会将函数挂起,也就是说async.await函数是一个挂起函数

什么是挂起函数?

suspend是协程的一个关键字,代表一个挂起函数,每个suspend函数只能在协程作用域中进行调用,关于协程作用域后面会讲解 前面我们已经使用了async.await挂起函数的操作。 挂起函数可以简单理解为:在不阻塞当前协程使用的线程的情况下,将协程挂起。 这里要理解好这句话就要清楚协程和线程之间的关系:

协程和线程有什么联系?

协程更倾向于一种设计模式,而线程则是一个进程的分支,是进程的一根树枝。 线程是并发的实现者,一个进程有多个线程,子线程之间并发执行,随机获取CPU时间片。 协程任务内部是通过线程来实现协程被挂起的情况下,并不代表执行的线程也被阻塞,可能这个线程会是空闲或者被其他任务所占用

说到线程,我们来讲下协程中的并发问题 协程并发?

我们来看下下面这个例子:


GlobalScope.launch(Dispatchers.Main) {
    for(index in 1 until 20){
        launch {
            println("index:$index")
        }
    }
}
打印结果:
index:1
index:2
index:3
...
...
index:18
index:19

看到这里按顺序打印出了1到19的值

我们将Dispatchers.Main改为我们将Dispatchers.IO

打印结果:
index:1
index:2
index:4
index:5
index:6
index:3
index:7
index:8
index:9
index:10
index:13
index:14
index:15
index:16
index:17
index:18
index:19
index:12
index:11

可以看到这里打印出的是一个无序的结果 为什么会出现这种情况呢? 这里就引出了协程调度器的概念, Dispatchers.Main:表示协程使用的是主线程 Dispatchers.IO:表示使用IO子线程操作

基于主线程的操作,只能等待上个协程执行完毕后,才能继续执行下个协程,所以打印出的是有序的 而基于IO线程的操作,可以并发状态下执行协程。所以打印的出的是无序的

总结

本篇文章对协程的一些基本操作做了讲解,下篇文章我们来讲下协程中的几个关键知识点 1.协程调度器CoroutlineDispatcher 2.线程上下文CoroutlineContext 3.协程启动模式CoroutlineStart 4.协程作用域CoroutlineScope 5.挂起函数以及suspend关键字的使用

关于协程的其他知识,请看后续文章

1.Android体系课之--Kotlin协程入门篇-协程的基本使用

2[.Android体系课之--Kotlin协程进阶篇-协程中关键知识点梳理]()

3.Android体系课之--Kotlin协程进阶篇-协程的异常处理机制以及suspend关键字详解

4.Android体系课之--Kotlin协程进阶篇-协程加Retrofit创建一个MVVM模式的网络请求框架

相关文章
|
24天前
|
Java 编译器 测试技术
Kotlin31 协程如何与 Java 进行混编?
Kotlin31 协程如何与 Java 进行混编?
23 2
Kotlin31 协程如何与 Java 进行混编?
|
23天前
|
XML 数据库 Android开发
探索Android开发:从入门到精通的旅程
在这篇文章中,我们将一起踏上一段激动人心的旅程,通过深入浅出的方式,解锁Android开发的秘密。无论你是编程新手还是有经验的开发者,本文都将为你提供宝贵的知识和技能,帮助你构建出色的Android应用。我们将从基础概念开始,逐步深入到高级技巧和最佳实践,最终实现从初学者到专家的转变。让我们开始吧!
31 3
|
2月前
|
存储 前端开发 测试技术
Android kotlin MVVM 架构简单示例入门
Android kotlin MVVM 架构简单示例入门
36 1
|
2月前
|
调度 Android开发 开发者
构建高效Android应用:探究Kotlin多线程优化策略
【10月更文挑战第11天】本文探讨了如何在Kotlin中实现高效的多线程方案,特别是在Android应用开发中。通过介绍Kotlin协程的基础知识、异步数据加载的实际案例,以及合理使用不同调度器的方法,帮助开发者提升应用性能和用户体验。
63 4
|
2月前
|
JSON 调度 数据库
Android面试之5个Kotlin深度面试题:协程、密封类和高阶函数
本文首发于公众号“AntDream”,欢迎微信搜索“AntDream”或扫描文章底部二维码关注,和我一起每天进步一点点。文章详细解析了Kotlin中的协程、扩展函数、高阶函数、密封类及`inline`和`reified`关键字在Android开发中的应用,帮助读者更好地理解和使用这些特性。
34 1
|
1月前
|
XML IDE Java
安卓应用开发入门:从零开始的旅程
【10月更文挑战第23天】本文将带领读者开启一段安卓应用开发的奇妙之旅。我们将从最基础的概念讲起,逐步深入到开发实践,最后通过一个简易的代码示例,展示如何将理论知识转化为实际的应用。无论你是编程新手,还是希望扩展技能的软件工程师,这篇文章都将为你提供有价值的指导和启发。
35 0
|
2月前
|
开发框架 移动开发 Android开发
安卓与iOS开发中的跨平台解决方案:Flutter入门
【9月更文挑战第30天】在移动应用开发的广阔舞台上,安卓和iOS两大操作系统各自占据半壁江山。开发者们常常面临着选择:是专注于单一平台深耕细作,还是寻找一种能够横跨两大系统的开发方案?Flutter,作为一种新兴的跨平台UI工具包,正以其现代、响应式的特点赢得开发者的青睐。本文将带你一探究竟,从Flutter的基础概念到实战应用,深入浅出地介绍这一技术的魅力所在。
91 7
|
2月前
|
Web App开发 编解码 视频直播
视频直播技术干货(十二):从入门到放弃,快速学习Android端直播技术
本文详细介绍了Android端直播技术的全貌,涵盖了从实时音视频采集、编码、传输到解码与播放的各个环节。文章还探讨了直播中音视频同步、编解码器选择、传输协议以及直播延迟优化等关键问题。希望本文能为你提供有关Andriod端直播技术的深入理解和实践指导。
56 0
|
2月前
|
Android开发 Kotlin
Android面试题之Kotlin中如何实现串行和并行任务?
本文介绍了 Kotlin 中 `async` 和 `await` 在并发编程中的应用,包括并行与串行任务的处理方法。并通过示例代码展示了如何启动并收集异步任务的结果。
34 0
|
2月前
|
Java 调度 Android开发
Android面试题之Kotlin中async 和 await实现并发的原理和面试总结
本文首发于公众号“AntDream”,详细解析了Kotlin协程中`async`与`await`的原理及其非阻塞特性,并提供了相关面试题及答案。协程作为轻量级线程,由Kotlin运行时库管理,`async`用于启动协程并返回`Deferred`对象,`await`则用于等待该对象完成并获取结果。文章还探讨了协程与传统线程的区别,并展示了如何取消协程任务及正确释放资源。
39 0
下一篇
DataWorks