企业网站策划方案网站建设方案,山西太原百度公司,wordpress删除顶部设置菜单,百浪科技做网站怎么样Kotlin的重要优势及特点之——结构化并发
Kotlin 协程让异步代码像阻塞代码一样易于使用。协程可大幅简化后台任务管理#xff0c;例如网络调用、本地数据访问等任务的管理。本主题介绍如何使用 Kotlin 协程解决以下问题#xff0c;从而让您能够编写出更清晰、更简洁的应用代…
Kotlin的重要优势及特点之——结构化并发
Kotlin 协程让异步代码像阻塞代码一样易于使用。协程可大幅简化后台任务管理例如网络调用、本地数据访问等任务的管理。本主题介绍如何使用 Kotlin 协程解决以下问题从而让您能够编写出更清晰、更简洁的应用代码。
所有源文件都必须编码为 UTF-8。
来源标注Android 上的 Kotlin 协程 | Android Developers
书接上篇Android Kotlin知识汇总二最佳实践-CSDN博客 Android 上的 Kotlin 协程 协程是一种并发设计模式可以在 Android 平台上使用它来简化异步执行的代码。 在 Android 上协程有助于管理长时间运行的任务。使用协程的专业开发者中有超过 50% 的人反映使用协程提高了工作效率。 简单来说协程就是一种轻量级的非阻塞的线程工具API可以用同步的方式写出异步的代码优雅地切换线程和处理回调地狱。与线程的关系线程在进程中协程在线程中。 协程特点
协程是我们在 Android 上进行异步编程的推荐解决方案。值得关注的特点包括
轻量可以在单个线程上运行多个协程因为协程支持挂起不会使正在运行协程的线程阻塞。挂起比阻塞节省内存且支持多个并行操作。内存泄漏更少使用结构化并发机制在一个作用域内执行多项操作。内置取消支持取消操作会自动在运行中的协程层次结构内传播。Jetpack 集成许多 Jetpack 库都包含提供全面协程支持的扩展。某些库还提供自己的协程作用域可供您用于结构化并发。
主流创建协程的方式
使用协程时在代码写法上和普通的顺序代码类似。创建协程可以使用以下三种方式
// 方法1使用 runBlocking 顶层函数
runBlocking {}// 方法2使用 GlobalScope 单例对象调用 launch 开启协程
GlobalScope.launch {}// 方法3创建 CoroutineScope 对象调用 launch 开启协程
val coroutineScope CoroutineScope(context)
coroutineScope.launch {}
方法 1 适用于单元测试场景实际开发中不推荐因为它是线程阻塞的方法 2 不会阻塞线程但它的生命周期会和 APP 一致且无法取消方法 3 推荐使用可以通过 context 参数去管理和控制协程的生命周期。
使用协程确保主线程安全
通过launch()创建一个新的协程空间{}内的代码块被叫做一个子协程。而传给 launch()的参数则用于指定执行这段代码运行的线程。
coroutineScope.launch(Dispatchers.IO) {//参数切到IO线程执行
}
coroutineScope.launch(Dispatchers.Main) {//参数切到主线程执行
} 在 Kotlin 中所有协程都必须在调度程序中运行即使它们在主线程上运行也是如此。 协程可以自行挂起而调度程序负责将其恢复。 Kotlin 提供了三个调度程序以用于指定应在何处运行协程
Dispatchers.Main - 使用此调度程序可在 Android 主线程上运行协程。此调度程序只能用于与界面交互和执行快速工作。Dispatchers.IO - 此调度程序经过了专门优化适合在主线程之外执行磁盘或网络 I/O。Dispatchers.Default - 此调度程序经过了专门优化适合在主线程之外执行占用大量 CPU 资源的工作。用例示例包括对列表排序和解析 JSON。
使用 withContext 方法 该方法支持自动切回原来的线程能够消除并发代码在协作时产生的嵌套。如果需要频繁地进行线程切换这种写法将有很大的优势即“使用同步的方式写异步代码”。如下所示
coroutineScope.launch(Dispatchers.Main) {// Dispatchers.Mainval image withContext(Dispatchers.IO) {// 切换到 IO 线程getImage(imageUrl) // 切换到 IO 线程}imageView.setImageBitmap(image) // Dispatchers.Main
}
withContext(Dispatchers.IO) 创建一个在 IO 线程池中运行的代码块。放在该块内的任何代码都始终通过 IO 调度程序执行。由于 withContext 本身就是一个挂起函数因此函数 getImage() 也是一个挂起函数。
使用 suspend 关键字
协程在常规函数的基础上添加了两项操作用于处理长时间运行的任务。
suspend 用于暂停执行当前协程并保存所有局部变量。resume 用于让已挂起的协程从挂起处继续执行。
代码在执行到某个 suspend 函数时会从正在执行它的线程上脱离协程会从被挂起的 suspend 函数指定的线程如 Dispatchers.IO中开始执行。当该 suspend方法执行完成之后会重新切换回它原先的线程。这个「切回来」的动作在 Kotlin 中叫做 resume。
在上述示例中把 withContext 单独放进一个getImage()里并使用 suspend 关键字标记才能编译通过示例代码如下
suspend fun getImage(imageUrl: String) // Dispatchers.MainwithContext(Dispatchers.IO) { // Dispatchers.IO (main-safety block)/* network IO here */ } // Dispatchers.Main
如果调用 suspend 函数只能从其他 suspend 函数进行调用时代码如下所示
suspend fun fetchDocs() { // Dispatchers.Mainval result getImage(url) // Dispatchers.IO
}
suspend fun getImage(url: String) withContext(Dispatchers.IO) { /* ... */ }
在上面的示例中getImage() 仍在主线程上运行但它会在启动网络请求之前挂起协程。当网络请求完成时getImage() 会恢复已挂起的协程fetchDocs()而不是使用回调通知主线程。
获取协程的返回值
启动协程的方式有两种
launch 启动新协程而不将结果返回给调用方。async 在另一个协程内或在挂起函数内且在执行“并行分解”时才使用并可以使用一个名为 await 的挂起函数返回结果。
警告launch 和 async 处理异常的方式不同。由于 async 对 await 进行最终调用因此它持有异常并将其作为 await 调用的一部分抛出。所以使用 async 会有把异常静默吞掉的风险。
并行分解 基于Kotlin 的结构化并发机制您可以定义启动一个或多个协程的 coroutineScope。然后您可以使用 await()或 awaitAll()保证这些协程在从函数返回结果之前完成。 例如在一个coroutineScope里执行两个并行的协程此时通过调用 await()对每个延迟引用就可以保证这两项 async 操作在返回值之前完成代码如下所示
suspend fun fetchTwoDocs() coroutineScope {val deferredOne async { fetchDoc(1) }val deferredTwo async { fetchDoc(2) }deferredOne.await()deferredTwo.await()}
此外coroutineScope 会捕获协程抛出的所有异常并将其传送回调用方。
协程的非阻塞式挂起 「非阻塞式挂起」指的就是协程在挂起的同时切线程这件事情。使用了协程的代码看似阻塞但由于协程内部做了很多工作包括自动切换线程它实际上是非阻塞的。 在代码执行的过程中当线程执行到了 suspend 方法就暂时不再执行剩余协程代码跳出协程的代码块。如果它是一个后台线程它会被系统回收或者再利用继续执行别的后台任务与 Java 线程池中的线程等同如果它是 Android 主线程它会继续执行界面刷新任务。 代码示例 使用协程模拟实现一个网络请求等待时显示 Loading请求成功或者出错让 Loading 消失并将状态反馈给用户。
依赖项信息
如需在 Android 项目中使用协程请将以下依赖项添加到应用的 build.gradle 文件中
dependencies {implementation(org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9)
}
ViewModel 代码
HiltViewModel
class MainViewModel Inject constructor() : ViewModel() {enum class RequestStatus {IDLE, LOADING, SUCCESS, FAIL}val requestStatus MutableStateFlow(RequestStatus.IDLE)/*** 模拟网络请求*/fun simulateNetworkRequest() {requestStatus.value RequestStatus.LOADINGviewModelScope.launch {val requestResult async { performSimulatedRequest() }.await()requestStatus.value if (requestResult) RequestStatus.SUCCESS else RequestStatus.FAIL}}/*** 模拟耗时操作随机数-成功或失败*/private suspend fun performSimulatedRequest() withContext(Dispatchers.IO) {delay(500)val random Random()return withContext random.nextBoolean()}}
MainActivity 代码
使用 Jetpack Compose将请求状态实时显示在界面上。代码如下所示
AndroidEntryPoint
class MainActivity : ComponentActivity() {//声明model属性private val mainViewModel: MainViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {ComposeTheme {Surface(modifier Modifier.fillMaxSize(),color MaterialTheme.colorScheme.background) {val requestStatusState mainViewModel.requestStatus.collectAsState()val requestStatus by rememberSaveable {requestStatusState }Text(text requestStatus.name,)}}}//请求网络mainViewModel.simulateNetworkRequest()}
} 小结 在 Kotlin 中协程就是基于线程来实现的一种更上层的工具 API在设计思想上协程是一个基于线程的上层框架。Kotlin 协程并没有脱离 Kotlin 或者 JVM 创造新的东西只是简化了多线程的开发。
下一篇继续介绍 Kotlin 协程在 Jetpack 实战开发过程中最有用的一些方面。