Kotlin编程第一课--(协程篇)23 异常:try-catch 坑!
这节课我们来学习 Kotlin 协程的异常处理。 其实到这里,我们就已经学完所有 Kotlin 协程的语法知识了。但在真正把 Kotlin 协程应用到生产环境之前,我们还需要掌握一个重要知识点,那就是异常处理。 比起 Kotlin 协程的语法知识点,协程的异常处理,其实更难掌握。在前面的课程中,我们已经了解到:协程就是互相协作的程序,协程是结构化的。正因为 Kotlin 协程有这两个特点,这就导致它的异常处理机制与我们普通的程序完全不一样。 换句话说:如果把 Java 里的那一套异常处理机制,照搬到 Kotlin 协程里来,你一定会四处碰壁。因为在普通的程序当中,你使用 try-catch 就能解决大部分的异常处理问题,但是在协程当中,根据不同的协程特性,它的异常处理策略是随之变化的。 我自己在工作中就踩过很多这方面的坑,遇到过各种匪夷所思的问题:协程无法取消、try-catch 不起作用导致线上崩溃率突然大增、软件功能错乱却追踪不到任何异常信息,等等。说实话,Kotlin...
Kotlin编程第一课--(协程篇)22 并发:协程不需要处理同步吗?
今天我们来讲讲协程的并发。 在大型软件的架构当中,并发也是一个不可避免的问题。然而,在传统的 Java 编程当中,并发却是个令人生畏的话题。因为 Java 的线程模型、内存模型、同步机制太复杂了,而当复杂的业务逻辑与复杂的并发模型混合在一起的时候,情况就更糟糕了!如果你用 Java 做过中大型软件,对此一定会深有体会。 我们都知道,Kotlin 的协程仍然是基于线程运行的。但是,经过一层封装以后,Kotlin 协程面对并发问题的时候,它的处理手段其实跟 Java 就大不一样。所以这节课,我们就来看看协程在并发问题上的处理,一起来探究下 Kotlin 协程的并发思路,从而真正解决并发的难题。 协程与并发在 Java 世界里,并发往往需要多个线程一起工作,而多线程往往就会有共享的状态,这时候程序就要处理同步问题了。很多初学者在这一步,都会把协程与线程的概念混淆在一起。比如你可以来看看下面这段代码,你觉得有多线程同步的问题吗? // 代码段1fun main() = runBlocking { var i = 0 // Default 线程池 ...
Kotlin编程第一课--(协程篇)21 select:到底是在选择什么?
今天我们来学习 Kotlin 协程的 select。 select,在目前的 Kotlin 1.6 当中,仍然是一个实验性的特性(Experimental)。但是,考虑到 select 具有较强的实用性,我决定还是来给你介绍一下它。 select 可以说是软件架构当中非常重要的一个组件,在很多业务场景下,select 与 Deferred、Channel 结合以后,在大大提升程序的响应速度的同时,还可以提高程序的灵活性、扩展性。 今天这节课,我会从 select 的使用角度着手,带你理解 select 的核心使用场景,之后也会通过源码帮你进一步分析 select API 的底层规律。学完这节课以后,你完全可以将 select 应用到自己的工作当中去。 好,接下来,我们就一起来学习 select 吧! select 就是选择“更快的结果” 由于 select 的工作机制比较抽象,我们先来假设一个场景,看看 select...
Kotlin编程第一课--(协程篇)20 Flow:为什么说Flow是“冷”的?
今天我们来学习 Kotlin 协程 Flow 的基础知识。 Flow,可以说是在 Kotlin 协程当中自成体系的知识点。Flow 极其强大、极其灵活,在它出现之前,业界还有很多质疑 Kotlin 协程的声音,认为 Kotlin 的挂起函数、结构化并发,并不足以形成核心竞争力,在异步、并发任务的领域,RxJava 可以做得更好。 但是,随着 2019 年 Kotlin 推出 Flow 以后,这样的质疑声就渐渐没有了。有了 Flow 以后,Kotlin 的协程已经没有明显的短板了。简单的异步场景,我们可以直接使用挂起函数、launch、async;至于复杂的异步场景,我们就可以使用 Flow。 实际上,在很多技术领域,Flow 已经开始占领 RxJava 原本的领地,在 Android 领域,Flow 甚至还要取代原本 LiveData 的地位。因为,Flow 是真的香啊! 接下来,我们就一起来学习 Flow。 Flow 就是“数据流”Flow 这个单词有“流”的意思,比如 Cash Flow 代表了“现金流”;Traffic Flow 代表了“车流”;Flow 在 Kotlin...
kotlin编程第一课--(协程篇)19 channel:为什么说channel是热的?
前面我们学习的挂起函数、async,它们一次都只能返回一个结果。但在某些业务场景下,我们往往需要协程返回多个结果,比如微信等软件的 IM 通道接收的消息,或者是手机 GPS 定位返回的经纬度坐标需要实时更新。那么,在这些场景下,我们之前学习的协程知识就无法直接解决了。 而今天我要讲解的 Kotlin 协程中的 Channel,就是专门用来做这种事情的。类似的需求,如果我们不使用 Channel 而是用其他的并发手段配合集合来做的话,其实也能实现,但复杂度会大大增加。那么接下来,我们就一起来学习下 Channel。 Channel 就是管道顾名思义,Channel 就是一个管道。我们可以用这个概念,先来建立一个思维模型: Channel 这个管道的其中一端,是发送方;管道的另一端是接收方。而管道本身,则可以用来传输数据。 所以,我们根据上面的思维模型,很容易就能写出下面的代码。 // 代码段1fun main() = runBlocking { // 1,创建管道 val channel = Channel<Int>() launch...
Kotlin编程第一课--(协程篇)题目解答 期中考试版本参考实现
上节课我给你布置了一份考试题,你完成得怎么样了呢?这节课呢,我会来告诉你我是如何用 Kotlin 来做这个图片处理程序的,供你参考。 由于上节课我们已经做好了前期准备,所以这里我们直接写代码就行了。 1.0 版本对于图片反转和裁切的这个问题,如果一开始你就去想象一个大图片,里面有几万个像素点,那你可能会被吓到。但是,如果你将数据规模缩小,再来分析的话,你会发现这个问题其实很简单。 这里,我们就以一张 4X4 像素的照片为例来分析一下。 这其实就相当于一个抽象的模型,如果我们基于这张 4X4 的照片,继续分析翻转和裁切,就会容易很多。我们可以来画一个简单的图形: 上面这张图,从左到右分别是原图、横向翻转、纵向翻转、裁切。其中,翻转看起来是要复杂一些,而裁切是最简单的。 我们先来处理裁切。对于裁切,其实只需要将图片当中某个部分的像素拷贝到内存,然后存储成为一张新图片就行了。 /** * 图片裁切 */fun Image.crop(startY: Int, startX: Int, width: Int, height: Int): Image { val...
Kotlin编程第一课--(协程篇)期中考试 用Kotlin实现图片处理程序
不知不觉间,咱们的课程就已经进行一半了,我们已经学完很多内容: 基础篇,我们学完了所有 Kotlin 基础语法和重要特性。 加餐篇,我们学习了 Kotlin 编程的 5 大编程思维:函数式思维、表达式思维、不变性思维、空安全思维、协程思维。 协程篇,我们也已经学完了所有基础的协程概念。 所以现在,是时候来一次阶段性的验收了。这次,我们一起来做一个图片处理程序,来考察一下自己对于 Kotlin 编程知识的理解和应用掌握情况。初始化工程的代码在这里GitHub,你可以像往常那样,将其 clone 下来,然后用 IntelliJ 打开即可。 我们仍然会分为两个版本 1.0、2.0,不过,这一次要轮到你亲自动手写代码了! 1.0 版本:处理本地图片当你将初始化工程打开以后,你会发现“src/main/resources/images/”这个目录下有一张图片:android.png,它就是我们要处理的图片对象。 一般来说,我们想要处理图片,会第一时间想到...
Kotlin编程第一课--(协程篇)18 实战:让KtHttp支持挂起函数
今天这节实战课,我们接着前面第 12 讲里实现的网络请求框架,来进一步完善这个 KtHttp,让它支持挂起函数。 在上一次实战课当中,我们已经开发出了两个版本的 KtHttp,1.0 版本的是基于命令式风格的,2.0 版本的是基于函数式风格的。其中 2.0 版本的代码风格,跟我们平时工作写的代码风格很不一样,之前我也说了,这主要是因为业界对 Kotlin 函数式编程接纳度并不高,所以这节课的代码,我们将基于 1.0 版本的代码继续改造。这样,也能让课程的内容更接地气一些,甚至你都可以借鉴今天写代码的思路,复用到实际的 Android 或者后端开发中去。 跟往常一样,这节课的代码还是会分为两个版本: 3.0 版本,在之前 1.0 版本的基础上,扩展出异步请求的能力。 4.0 版本,进一步扩展异步请求的能力,让它支持挂起函数。 好,接下来就正式开始吧! 3.0 版本:支持异步(Call)有了上一次实战课的基础,这节课就会轻松一些了。关于动态代理、注解、反射之类的知识不会牵涉太多,我们今天主要把精力都集中在协程上来。不过,在正式开始写协程代码之前,我们需要先让 KtHttp...
Kotlin编程第一课--(协程篇)17 Context:万物皆为Context?
今天我们来学习 Kotlin 协程的 Context。 协程的 Context,在 Kotlin 当中有一个具体的名字,叫做 CoroutineContext。它是我们理解 Kotlin 协程非常关键的一环。 从概念上讲,CoroutineContext 很容易理解,它只是个上下文而已,实际开发中它最常见的用处就是切换线程池。不过,CoroutineContext 背后的代码设计其实比较复杂,如果不能深入理解它的设计思想,那我们在后面阅读协程源码,并进一步建立复杂并发结构的时候,都将会困难重重。 所以这节课,我将会从应用的角度出发,带你了解 CoroutineContext 的使用场景,并会对照源码带你理解它的设计思路。另外,知识点之间的串联也是很重要的,所以我还会带你分析它跟我们前面学的 Job、Deferred、launch、async 有什么联系,让你能真正理解和掌握协程的上下文,并建立一个基于 CoroutineContext 的协程知识体系。 Context 的应用前面说过,CoroutineContext 就是协程的上下文。你在前面的第 14~16...
Kotlin编程第一课--(协程篇)16 Job:协程也有生命周期吗?
今天我们来学习 Kotlin 协程的 Job。 Job 其实就是协程的句柄。从某种程度上讲,当我们用 launch 和 async 创建一个协程以后,同时也会创建一个对应的 Job 对象。另外,Job 也是我们理解协程生命周期、结构化并发的关键知识点。通过 Job 暴露的 API,我们还可以让不同的协程之间互相配合,从而实现更加复杂的功能。 虽然前面已经解释过,Job 就是协程的句柄,但你可能还是不清楚它到底是什么,因为句柄本身就是一个比较“虚”的概念。所以在这节课中,我们会从使用的角度入手,来看看 Job 到底能干什么。在充分理解了 Job 的用法以后,我们再来结合它的源代码进一步分析,这样对 Job 也会有一个更加清晰的认知。 Job 生命周期在上节课我们学习 launch、async 的时候,我们知道它们两个返回值类型分别是 Job 和 Deferred。 // 代码段1public interface Deferred<out T> : Job { public suspend fun await(): T} 而如果你去看...