Android 中使用 Kotlin 协程
Android 上的 Kotlin 协程
协程是一种并发设计模式,您可以在 Android 平台上使用它来简化异步执行的代码。协程是在版本 1.3 中添加到 Kotlin 的,它基于来自其他语言的既定概念。
协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括:
- 内存泄漏更少:使用结构化并发机制在一个作用域内执行多项操作。
简单讲,kotlin 协程是一个轻量级的并发编程框架,跟 RxJava 类似。
向 Android 项目中项目添加协程
要在 Kotlin 中使用协程,您必须在项目的 build.gradle (Module: app) 文件中添加 coroutines-core 库。
Android 上的协程作为核心库和 Android 专用扩展函数提供:
- kotlinx-coroutines-core - 用于在 Kotlin 中使用协程的主接口
- kotlinx-coroutines-android - 在协程中支持 Android 主线程
此初始应用已在 build.gradle. 中包含依赖项。创建新的应用项目时,您需要打开 build.gradle (Module: app) 并将协程依赖项添加到项目中。
dependencies { ... implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:x.x.x" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:x.x.x"}您可以在 Kotlin 协程版本页面上找到协程库的最新版版本号,以替代“xxx”。
使用协程进行网络请求
使用协程移除回调
Kotlin 协程使您能够将基于回调的代码转换为顺序代码。顺序编写的代码通常更易于阅读,甚至可以使用异常等语言功能。
关键字 suspend 是 Kotlin 将函数(即函数类型)标记为可供协程使用的方式。当协程调用标记为 suspend 的函数时,它不会像常规函数调用一样在函数返回之前进行阻塞,而是挂起执行,直到结果就绪为止,然后从上次停止的位置恢复并使用返回的结果。当它挂起并等待结果时,它会取消阻塞正在运行它的线程,以便其他函数或协程可以运行。
suspend fun searchPhotos(): Result<List<Photo>> { return withContext(dispatcher) { try { Result.Success(RetrofitFamily.createService(PexelsApi::class.java).queryPhotos("art", 1, 10).photos) } catch (throwable: Throwable) { when (throwable) { is IOException -> Result.NetworkError is HttpException -> { Result.Error(throwable.code(), throwable.response()?.errorBody()?.string()) } else -> Result.Error(null, throwable.message) } } } }CoroutineScope
在 Kotlin 中,所有协程都在 CoroutineScope 中运行。作用域在其整个作业期间会控制协程的生命周期。如果取消某个作用域的作业,则该作用域内启动的所有协程也将取消。在 Android 上,在一些情况下,例如当用户离开 Activity 或 Fragment 时,您可以使用作用域取消所有正在运行的协程。作用域还允许您指定默认调度程序。调度程序可以控制哪个线程运行协程。
对于界面启动的协程,通常在 Dispatchers.Main(Android 上的主线程)上启动这类协程是正确的。在 Dispatchers.Main 上启动的协程在挂起期间不会阻塞主线程。由于 ViewModel 协程几乎总是在主线程上更新界面,因此在主线程上启动协程可避免额外的线程切换。在主线程上启动的协程可在启动后随时切换调度程序。例如,它可以使用另一个调度程序从主线程外解析大型 JSON 结果。
使用 viewModelScope
AndroidX lifecycle-viewmodel-ktx 库将 CoroutineScope 添加到已配置为启动界面相关协程的 ViewModel 中。viewModelScope 添加为 ViewModel 类的扩展函数。此作用域绑定到 Dispatchers.Main,并会在清除 ViewModel 后自动取消。
fun searchPhotos() { photosLiveData.postValue(Result.Loading) viewModelScope.launch { photosLiveData.postValue(photosRepository.searchPhotos()) } }完整示例可参考 demo-coroutine