史上最详Android版kotlin协程入门进阶实战(一),2024最新Android开发面试大全

为了方便我们在此文章把kotlin协程简称为协程(Coroutine)。

协程是什么

很多人听到协程的时候第一想法就是协程是什么,笔者在这里也不会去给它下定义,推荐您移步带kotlin官方网站去找相关解释。但是我们这里引用官方的一段原话:

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

简单的概括就是我们可以,以同步的方式去编写异步执行的代码。协程是依赖于线程,但是协程挂起时不需要阻塞线程,几乎是无代价的。所以协程像是一种用户态的线程,非常轻量级,一个线程中可以创建N个协程。协程的创建是过CoroutineScope创建,协程的启动方式有三种:

  1. runBlocking:T 启动一个新的协程并阻塞调用它的线程,直到里面的代码执行完毕,返回值是泛型T,就是你协程体中最后一行是什么类型,最终返回的是什么类型T就是什么类型。

  2. launch:Job 启动一个协程但不会阻塞调用线程,必须要在协程作用域(CoroutineScope)中才能调用,返回值是一个Job。

  3. async:Deferred<T> 启动一个协程但不会阻塞调用线程,必须要在协程作用域(CoroutineScope)中才能调用。以Deferred对象的形式返回协程任务。返回值泛型TrunBlocking类似都是协程体最后一行的类型。

等等,好像哪里不对,奇怪的知识点突然有点增多啊。

上面提到协程体中最后一行是什么类型,最终返回的是什么类型T就是什么类型,好像跟我们想的不一样,返回值不应该是用return吗,学过kotlin的会知道,在的kotlin高阶函数中,lambda表达式如果你没有显式返回一个值,那它将隐式返回最后一个表达式的值。

JobDeferred协程作用域又是些啥玩意!

不急,慢慢来,我们一个一个的来解释清楚。

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


Job我们可以认为他就是一个协程作业是通过CoroutineScope.launch生成的,同时它运行一个指定的代码块,并在该代码块完成时完成。我们可以通过isActiveisCompletedisCancelled来获取到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 |

我们可以通过下图可以大概了解下一个协程作业从创建到完成或者取消,Job在这里不扩展了,后面我们会在实际使用过程中去讲解。

wait children

±----+ start ±-------+ complete ±------------+ finish ±----------+

| New | -----> | Active | ---------> | Completing | -------> | Completed |

±----+ ±-------+ ±------------+ ±----------+

| cancel / fail |

| ±---------------+

| |

V V

±-----------+ finish ±----------+

| Cancelling | --------------------------------> | Cancelled |

±-----------+ ±----------+

Deferred

Deferred继承自Job,我们可以把它看做一个带有返回值的Job

public interface Deferred : Job {

//返回结果值,或者如果延迟被取消,则抛出相应的异常

public suspend fun await(): T

public val onAwait: SelectClause1

public fun getCompleted(): T

public fun getCompletionExceptionOrNull(): Throwable?

}

我们需要重点关注await()方法,可以看到await()方法返回结果是T,说明我们可以通过await()方法获取执行流的返回值,当然如果出现异常或者被取消执行,则会抛出相对应的异常。

什么是作用域

协程作用域(Coroutine Scope)是协程运行的作用范围。launchasync都是CoroutineScope扩展函数CoroutineScope定义了新启动的协程作用范围,同时会继承了他的coroutineContext自动传播其所有的 elements和取消操作。换句话说,如果这个作用域销毁了,那么里面的协程也随之失效。就好比变量的作用域,如下面test方法中的money变量

private fun test(){ // scope start

int money = 100;

println(money)

} // scope end

// println(money)

此时money是不能被调用,因为AS会提示 Unresolved reference: money。协程作用域也是这样一个作用,可以用来确保里面的协程都有一个作用域的限制。我们开发过程中最常见的场景就内存泄露,协程同样存在这样的问题,后面我们再细细讲解协程作用域CoroutineScope的相关知识,这里只是作为基础点讲解,不继续往下延伸。

Kotlin协程的基础用法

============================================================================

现在我们开始使用协程,首先我们在MainActivity的xml布局中新建一个Button按钮然后设置好点击事件,然后创建一个start()方法,通过Button的点击事件执行。现在我们开始在start方法中使用协程。

刚才我们上面提到启动协程有三种方式,接下来我们先看看如何通过runBlockinglaunchasync启动协程,我们直接在start方法中使用,但是由于我们的launchasync启动,只能在协程的作用域下启动,那我们又该怎么办呢?

运行第一个协程


在Android中有一个名为GlobalScope全局顶级协程,这个协程是在整个应用程序生命周期内运行的。我们就以此协程来使用launchasync启动,代码如下:

import android.os.Bundle

import android.util.Log

import android.view.View

import android.widget.Button

import android.widget.LinearLayout

import androidx.appcompat.app.AppCompatActivity

import androidx.constraintlayout.widget.Group

import androidx.viewpager.widget.ViewPager

import kotlinx.coroutines.*

import java.lang.NullPointerException

class MainActivity : AppCompatActivity() {

private lateinit var btn:Button

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btn = findViewById(R.id.btn)

btn.setOnClickListener {

start()

}

}

private fun start(){

runBlocking {

Log.d(“runBlocking”, “启动一个协程”)

}

GlobalScope.launch{

Log.d(“launch”, “启动一个协程”)

}

GlobalScope.async{

Log.d(“async”, “启动一个协程”)

}

}

}

然后运行app,点击按钮执行start()方法。我们就可以在控制台上看到如下输出:

D/runBlocking: 启动一个协程

D/launch: 启动一个协程

D/async: 启动一个协程

image.png

,so easy。协程原来这么简单,那我们接着继续往下走。上面提到过三种启动方式分别会的得到各自的返回信息。我们现在增加三个变量然后分别用协程进行赋值,同时进行输出:

private fun start(){

val runBlockingJob = runBlocking {

Log.d(“runBlocking”, “启动一个协程”)

}

Log.d(“runBlockingJob”, “$runBlockingJob”)

val launchJob = GlobalScope.launch{

Log.d(“launch”, “启动一个协程”)

}

Log.d(“launchJob”, “$launchJob”)

val asyncJob = GlobalScope.async{

Log.d(“async”, “启动一个协程”)

“我是返回值”

}

Log.d(“asyncJob”, “$asyncJob”)

}

然后运行,我们可以在控制台上看到如下输出:

D/runBlocking: 启动一个协程

D/runBlockingJob: 41

D/launchJob: StandaloneCoroutine{Active}@3b8b871

D/launch: 启动一个协程

D/async: 启动一个协程

D/asyncJob: DeferredCoroutine{Active}@63f265

也有可能是

D/runBlocking: 启动一个协程

D/runBlockingJob: 41

D/launchJob: StandaloneCoroutine{Active}@1344515

D/asyncJob: DeferredCoroutine{Active}@38c002a

D/async: 启动一个协程

D/launch: 启动一个协程

还有可能是

D/runBlocking: 启动一个协程

D/runBlockingJob: 41

D/launch: 启动一个协程

D/launchJob: StandaloneCoroutine{Active}@b94e973

D/async: 启动一个协程

D/ asyncJob: DeferredCoroutine{Active}@f7aa030

嗯哼,什么情况

怎么后面4条日志顺序还是随机的。没有看懂的童鞋,说明你没有仔细看上面的文字。

知识点来了,赶紧拿出你的小本本记下来,我们一个一个的来分析。

我们在上面提到过runBlocking启动的是一个新的协程并阻塞调用它的线程,我们对比输出日志可以看到前两条runBlocking的相关输出日志的位置顺序是不会变化的,这就证明我们之前所说的runBlocking会阻塞调用它的线程,直到runBlocking运行结束才继续往下执行。

接下来我们再继续往下看,我们看到后面四条日志是无序的,但是launchJob始终在asyncJob前面。而launchasync协程体内的日志输出是无序的。每执行一次看到的顺序都有可能跟之前的不一样。我们前面提到过launchasync都是启动一个协程但不会阻塞调用线程,所以launchJob始终在asyncJob前面(2个协程之间不是很明显,你们自己在尝试的时候,可以同时启动5个甚至更多协程去看日志输出)

launchasync协程体内的日志是无序的,这是因为协程采用的是并发设计模式,所以launchasync的协程体内的log日志输出是无序方式,这就解释了launchasync都是启动一个协程但不会阻塞调用线程,同时也解释了log日志之间输出顺序之间的关系(这里描述是不严谨的,后面会补充)。

难道就这样结束了吗?那你想的可就太简单了。刚才我们提到协程采用的是并发设计模式,多个协程并发执行的。那如果这个时候,我们把启动协程放在同一协程作用域下启动的是顺序又该是怎么样的呢? 大家可以先思考一下这个问题,回头我们再来看这个问题。

runBlocking的返回值


现在我们回到之前的话题,我们看到输出的日志信息中runBlockingJob的输出结果是41,为什么是这么一个数值,其实他默认返回是一个该协程作业的当前状态

image.png

我们通过runBlocking方法可以看到,其返回值是调用了joinBlocking方法,而在joinBlocking方法中

我们看到joinBlocking方法返回了一个state强转成泛型T类型。我们现在大概知道runBlocking返回的是个什么东西了。如果在runBlocking协程最后一行增加一个返回值:

val runBlockingJob = runBlocking {

Log.d(“Coroutine”, “runBlocking启动一个协程”)

“我是runBlockingJob协程的返回值”

}

我们将会看到如下输出:

D/Coroutine: runBlocking启动一个协程

D/runBlockingJob: 我是runBlockingJob协程的返回值

runBlocking它的设计目的是将常规的阻塞代码连接到一起,主要用于main函数和测试中。根据本文章的目标我们后续将不再往下扩展。

继续往下走,我们看到launchJob输出的是一个StandaloneCoroutine对象,为什么会是一个StandaloneCoroutine对象呢,不是说好的返回一个Job吗?

别慌,稳住!继续往下看

launch函数


我们看到launch函数中有3个参数contextstartblock,同时都带有默认值,虽然我们不知道这三个参数是干什么用的,但是我们可以看名知其意,不妨先大胆的猜测一下,我们这里先跳过,后面再针对这三个参数做一些基本讲解。我们看到launch方法最终返回的是一个coroutine对象,由于我们没有传入值其最后返回的是一个StandaloneCoroutine对象,跟我们输出的日志结果一致。那为什么笔者会说launch返回的是一个Job呢。我们再继续看看StandaloneCoroutine又是一个什么鬼,通过查找继承关系我们可以看到,StandaloneCoroutine就是一个Job,现在就一目了然了。

private open class StandaloneCoroutine(…) : AbstractCoroutine(parentContext, active){

//此处省略…

}

public abstract class AbstractCoroutine(…) : JobSupport(active), Job, Continuation, CoroutineScope {

//此处省略…

}

async函数


同理我们也看看async函数,和launch拥有同样的3个参数contextstartblock,默认值都是一样的,最终返回的是也是一个coroutine对象。只是async返回的DeferredCoroutine对象。

private open class DeferredCoroutine(…) : AbstractCoroutine(parentContext, active), Deferred, SelectClause1 {

//此处省略…

}

同样的都是继承 AbstractCoroutine<Unit>类,但是DeferredCoroutine同时也继承Deferred<T>接口。这么看来DeferredCoroutine就是一个Deferred<T>,一个携带有返回值Job。那么问题来了,我们要怎么获取到这个Deferred<T>携带的返回值T呢。

我们在一开始的时候提到需要重点关注Deferredawait()方法,我们可以通过返回Deferred对象,调用await()方法来获取返回值,我们看到await()前面有个suspend关键字,这又是个额啥玩意。

public suspend fun await(): T

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

  • Android前沿技术大纲

  • 全套体系化高级架构视频

资料领取:点赞+点击GitHub免费获取

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

含大厂面经、学习笔记、源码讲义、实战项目、讲解视频**
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
[外链图片转存中…(img-pBluZ3Tb-1711083526741)]

最后

针对Android程序员,我这边给大家整理了一些资料,包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

  • Android前沿技术大纲

    [外链图片转存中…(img-IiLfJIQr-1711083526742)]

  • 全套体系化高级架构视频

    [外链图片转存中…(img-J97JTc6R-1711083526742)]

资料领取:点赞+点击GitHub免费获取

往期Android高级架构资料、源码、笔记、视频。高级UI、性能优化、架构师课程、混合式开发(ReactNative+Weex)全方面的Android进阶实践技术,群内还有技术大牛一起讨论交流解决问题。

  • 16
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Kotlin是一种现代的、类型安全的编程语言,具有简洁、高效、可读性强等特点,号称是Java的改进。本文将介绍Kotlin入门进阶实战方法。 1. 入门篇 首先,你需要了解Kotlin的语法及基础语法,包括变量、函数、控制语句等。可以通过学习Kotlin官方文档或者基础教程进行学习。完成基础语法学习后,可以尝试编写一些小的Kotlin程序。 2. 进阶篇 在掌握了Kotlin的基础知识后,需要深入学习Kotlin的高级语法,例如Lambda表达式、对象表达式与对象声明、扩展函数及属性等。这些高级语法可以帮助你更好地利用Kotlin提供的高级特性,提高代码的复用性和可读性。 3. 实战篇 最后,实践是检验技能的最好方法。可以尝试使用Kotlin进行实际项目的开发,例如移动端App开发、Web后端开发等。在实际项目,你可以充分运用Kotlin的特性,比如使用Kotlin的Java互操作性特性以及Kotlin与Spring、Android等框架的集成,来提高开发效率和代码质量。 总之,Kotlin学习需要注意多练习、多实践,并且不断更新Kotlin本,了解其最新的特性和使用方法,从而提升自己的编程水平。 ### 回答2: Kotlin是一种静态类型的编程语言,它是由JetBrains公司开发的。Kotlin语言同时支持面向对象编程和函数式编程,其设计的目标是成为一种更简洁、安全和实用的语言。Kotlin可以用于开发Android应用程序、Web应用程序、桌面应用程序、服务器端应用程序和大数据应用程序。 Kotlin入门学习需要掌握其基本语法、基本数据类型、函数和控制流等概念,可以通过学习教程、视频、练习题和实践项目等方式来进行学习。学习过程要注意掌握Kotlin与Java的区别,例如空值处理、lambda表达式等方面的差异。 在进阶学习阶段,可以学习Kotlin的高级语法、面向对象编程和函数式编程的特性、Kotlin与Java互操作、协程等内容。此外,掌握Kotlin标准库和第三方库的使用也是进阶学习的重要内容。 在实战环节,可以通过实践项目来深入掌握Kotlin的应用,提高自己的编程能力。例如,可以通过开发Android应用程序、Web应用程序和桌面应用程序等项目,来实践Kotlin的应用。 总之,Kotlin入门进阶需要学习者进行系统性、持续性的学习,通过理论学习和实践项目相结合的方式,来全面掌握Kotlin的应用。 ### 回答3: Kotlin 是一门兼具面向对象编程和函数式编程特性的现代化编程语言。它既能够用于 Android 开发,也能够用于 Web 开发、桌面应用程序的开发等领域。 对于 Kotlin 入门级的学习者,可以通过在线教程、视频教程、博客等途径了解 Kotlin 的基本语法、函数、面向对象编程等知识。可以先从一些简单的练习开始,例如编写 Hello World 程序、定义变量、实现条件判断等。 当进入到 Kotlin进阶阶段后,需要深入学习函数式编程、泛型、集合、类型推断、异常处理等知识。这时候,需要更深入地了解 Kotlin 的一些高级特性,例如 Lambda 表达式、闭包、协程等。 Kotlin 实战的部分就需要将所学知识运用到真实的开发场景。可以通过编写简单的 Android 应用、Web 应用程序、桌面应用程序等来进行实践。需要学习一些与领域相关的知识,例如 Android 开发的界面设计、网络请求、数据存储等。同时也要了解一些开发工具和框架,例如 Android Studio、Ktor、Spring、Hibernate 等,以便更高效地完成项目开发。 总结来说,从入门进阶实战需要不断学习、实践和精进。需要在实践不断优化自身开发方式,提高代码的可读性和可维护性,掌握 Kotlin 的各种高级特性,为自己更好地应对开发场景提供基础。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值