多线程和回调入门指南

“使用 Kotlin 开发 Android 应用”课程假定您熟悉多线程的概念和术语。本页用于对此前的学习内容进行简要说明和回顾。

移动设备具有处理器,而现在大多数设备都具有多个硬件处理器,各个处理器同时运行进程。我们称之为“多进程”。

为了更高效地使用处理器,操作系统可以使应用在一个进程内创建多个执行线程。我们称之为“多线程”。

图片

您可以把这想象成同时阅读多本图书,每阅读完一章节后就切换到另一本,最后读完所有图书,但您不可能在同一时刻阅读多本图书。

管理所有这些线程需要一些基础架构。

图片

调度程序会考虑优先顺序等问题,并确保所有线程都能够运行并完成。它不会允许任何一本书永远放在书架上攒灰尘。但是,如果某本书的内容过长,或者可以放到后面阅读,则可能要过一段时间才会送到您手中。

调度程序会设置线程,也就是说,它会向您发送需要阅读的图书,并指定进行阅读的上下文您可以将上下文想象成一个独立的专用阅读室。有些上下文最适合界面操作,而有些上下文则专门用于处理输入/输出操作。

唯一需要知道的是,面向用户的应用通常有一个在前台运行的主线程。主线程可以调度在后台运行的其他线程。

在 Android 中,主线程是一个处理所有界面更新的线程。此主线程(也称为界面线程)也是调用所有点击处理程序和其他界面和生命周期回调的线程。界面线程是默认线程。除非应用显式切换线程或使用在其他线程上运行的类,否则应用的所有操作都在主线程上执行。

这会带来潜在的挑战。界面线程必须顺畅运行才能确保出色的用户体验。为了避免用户在使用您的应用时感觉到任何卡顿,主线程必须每 16 毫秒或更频繁地更新屏幕,或者以大约每秒 60 帧的频率更新屏幕。在这种速度下,人类感受到的帧变化是完全流畅的。要达到这种效果,就需要在很短的时间内处理大量的帧。因此,在 Android 上,避免阻塞界面线程是非常必要的。在这种情况下,阻塞意味着界面线程在等待数据库更新等操作完成时完全不执行任何操作。

图片

许多常见任务耗时超过 16 毫秒,例如从互联网获取数据、读取大型文件或向数据库写入数据。因此,通过调用代码来执行与主线程类似的任务可能会导致应用暂停、卡顿甚至冻结。如果您阻塞主线程太久,应用甚至可能会崩溃并显示一个“应用无响应”(ANR) 对话框。

回调

对于从主线程之外完成工作,您有几种选择。

在不阻塞主线程的情况下执行长时间运行的任务的一种模式是回调通过使用回调,您可以在后台线程上启动长时间运行的任务。任务完成后,系统会调用回调(以参数形式提供),以便将结果告知主线程上的代码。

回调是一种很好的模式,但也存在缺点。过多使用回调的代码可能会变得难以读取和推演。因为虽然代码看起来很有序,但回调代码会在未来某个时间异步运行。此外,回调也不允许使用某些语言功能,例如异常。

协程

在 Kotlin 中,协程是用于顺畅、高效地处理长时间运行的任务的解决方案。Kotlin 协程使您能够将基于回调的代码转换为顺序的代码。顺序编写的代码通常更易于阅读,甚至可以使用异常等语言功能。最后,协程和回调执行的是完全相同的操作:等待长时间运行的任务获得结果,然后继续执行任务。