Kotlin 中 CoroutineScope 和 coroutineScope 的区别
Kotlin 文档将协程定义为可以暂停的执行,因为它正在等待执行某些后台任务,例如从网络下载资源。
协程帮助我们实现并发,因为当协程挂起时其他计算继续执行。 请注意,协程独立于它们正在使用的线程,因为不能保证它们会在它们正在使用的线程上恢复执行。
要创建一个新的协程,我们必须在一个范围内进行。 在本教程中,我们将学习非结构化并发中的范围如何影响子协程以及如何使用结构化并发解决范围问题。
创建一个新的 Kotlin 项目
本教程将使用 IntelliJ IDEA,但您可以使用任何首选的开发环境。
打开 IntelliJ IDEA 并选择文件 > 新建 > 项目。 在打开的窗口中,选择左下方的Android,然后在右侧选择Empty activity,如下图。
按标有 Next 的按钮,在打开的窗口中,输入项目名称 CoroutineScope,输入包名称 com.coffeedev.coroutinescope,在 Language 部分选择 Kotlin,并在 Minimum SDK 部分选择 API 19。
确保这些详细信息如下所示。
按标有 Create 的按钮生成一个新的 Android 项目。 此操作创建一个新应用程序,其中包含一个名为 MainActivity 的活动和一个名为 activity_main 的布局。
我们将使用这些文件来测试我们在本教程中介绍的示例。 确保您有有效的互联网连接,以便将所需的依赖项添加到我们的应用程序。
要使用协程,我们需要将 kotlinx-coroutines-core 依赖项添加到我们的项目中。 将以下依赖复制并粘贴到 build.gradle 文件中以添加协程依赖。
dependencies {
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
}
在主布局上创建一个按钮
打开 src/main/res/layout 下的 acivity_main.xml 布局文件,将以下代码复制粘贴到文件中。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal"
tools:context=".MainActivity" >
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="100dp"
android:text="@string/button_text"/>
</LinearLayout>
此代码创建一个 LinearLayout,其中仅包含一个 Button 类型的视图。 我们将使用此按钮来调用我们应用程序中的协程。
确保最终布局如下所示。
该按钮使用文本属性指示的字符串资源进行标记。 将以下字符串资源复制并粘贴到 src/main/res/values 文件夹下名为 strings.xml 的文件中。
这为我们的按钮创建了一个文本,并且可以使用名称 button_text 访问该文本。
<resources>
<string name="app_name">CoroutineScope</string>
<string name="button_text">Press Me</string>
</resources>
在 Kotlin 中使用 CoroutineScope
在介绍部分,我们提到要创建一个新的协程,我们必须在一个范围内进行。 这就是 CoroutineScope 的用武之地。
要查看实际效果,请将以下代码复制并粘贴到 src/main/java/com/coffeedev/coroutinescope 文件夹下的 MainActivity.kt 文件中。
package com.coffeedev.coroutinescope
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.button)
button.setOnClickListener {
CoroutineScope(Dispatchers.Main).launch {
val message = getLoopProduct();
Toast.makeText(applicationContext, "Message: $message", Toast.LENGTH_LONG)
.show();
}
}
}
private suspend fun getLoopProduct(): Int {
var value = 1;
CoroutineScope(Dispatchers.IO).launch {
for (number in 1..5) {
delay(15);
value *= number;
}
}
return value;
}
}
在这段代码中,我们创建了一个名为 getLoopProduct()
的挂起函数,它返回 for 循环的结果。 for 循环使用协程执行,该协程使用 Dispatchers.IO 线程运行,作为 CoroutineScope() 的参数传递。
对于 for 循环的每次迭代,都有 15 毫秒的延迟,挂起当前正在执行的线程。
在 onCreate()
生命周期方法中,我们创建了一个使用 Dispatchers.Main 线程运行的新范围,它只是主线程。 请注意,getLoopProduct()
的协程是在 onCreate()
方法内创建的协程的子级,因为在其中调用了挂起函数。
从不同范围创建的协程独立运行。 由于子协程使用与父协程不同的作用域,因此父协程不会等待子协程完成执行。
这种类型的执行称为非结构化并发。
我们的 onCreate() 方法中的协程只执行一次并终止。 这意味着子协程继续在后台运行,并可能使我们的应用程序面临内存泄漏。
我们使用在布局中创建的按钮来显示包含 getLoopProduct() 返回值的 Toast。 setOnClickListener() 方法在我们按下按钮时在屏幕上显示 Toast。
运行此代码并注意 Toast 显示值 1,因为在子协程完成执行之前父协程已挂起。
输出:
在 Kotlin 中使用 coroutineScope
CoroutineScope()
和 coroutineScope()
之间的区别在于后者创建一个新的作用域而不创建一个新的协程。 子协程使用父协程范围,确保它在父协程完成执行之前完成。
这种类型的执行称为结构化并发。
要查看实际效果,请将上一个示例中的挂起函数替换为下面提供的函数。
private suspend fun getLoopProduct(): Int {
var value = 1;
coroutineScope {
for (number in 1..5) {
delay(15);
value *= number;
}
}
return value;
}
由于 onCreate()
方法中的代码没有变化,子协程使用父作用域,运行在主线程中执行for循环。 父协程在终止之前等待子协程执行 for 循环。
运行这段代码,注意 Toast 显示的值为 1 20。二表示子协程正在执行整个循环,由于重用父作用域而没有终止。
输出:
总结
在本文中,我们了解了非结构化并发的范围如何影响子协程以及如何使用结构化并发解决问题。 我们涵盖的主要主题是如何使用 CoroutineScope()
创建独立作用域以及如何使用 coroutineScope()
重用父作用域。
相关文章
如何在 Java 中延迟几秒钟的时间
发布时间:2023/12/17 浏览次数:217 分类:Java
-
本篇文章主要介绍如何在 Java 中制造程序延迟。本教程介绍了如何在 Java 中制造程序延时,并列举了一些示例代码来了解它。
如何在 Java 中把 Hashmap 转换为 JSON 对象
发布时间:2023/12/17 浏览次数:187 分类:Java
-
它描述了允许我们将哈希图转换为简单的 JSON 对象的方法。本文介绍了在 Java 中把 Hashmap 转换为 JSON 对象的方法。我们将看到关于创建一个 hashmap,然后将其转换为 JSON 对象的详细例子。
如何在 Java 中按值排序 Map
发布时间:2023/12/17 浏览次数:171 分类:Java
-
本文介绍了如何在 Java 中按值对 Map 进行排序。本教程介绍了如何在 Java 中按值对 Map
进行排序,并列出了一些示例代码来理解它。
如何在 Java 中打印 HashMap
发布时间:2023/12/17 浏览次数:192 分类:Java
-
本帖介绍了如何在 Java 中打印 HashMap。本教程介绍了如何在 Java 中打印 HashMap 元素,还列举了一些示例代码来理解这个主题。
在 Java 中更新 Hashmap 的值
发布时间:2023/12/17 浏览次数:146 分类:Java
-
本文介绍了如何在 Java 中更新 HashMap 中的一个值。本文介绍了如何在 Java 中使用 HashMap 类中包含的两个方法-put() 和 replace() 更新 HashMap 中的值。
Java 中的 hashmap 和 map 之间的区别
发布时间:2023/12/17 浏览次数:79 分类:Java
-
本文介绍了 Java 中的 hashmap 和 map 接口之间的区别。本教程介绍了 Java 中 Map 和 HashMap 之间的主要区别。在 Java 中,Map 是用于以键值对存储数据的接口,
在 Java 中获取用户主目录
发布时间:2023/12/17 浏览次数:218 分类:Java
-
这篇文章向你展示了如何在 Java 中获取用户主目录。本教程介绍了如何在 Java 中获取用户主目录,并列出了一些示例代码以指导你完成该主题。
Java 中 size 和 length 的区别
发布时间:2023/12/17 浏览次数:179 分类:Java
-
这篇文章教你如何知道 Java 中大小和长度之间的区别。本教程介绍了 Java 中大小和长度之间的区别。我们还列出了一些示例代码以帮助你理解该主题。
Java 中的互斥锁
发布时间:2023/12/17 浏览次数:111 分类:Java
-
了解有关 Java 中互斥锁的一切,在计算机科学领域,互斥或互斥被称为并发控制的属性。每台计算机都使用称为线程的最小程序指令序列。有一次,计算机在一个线程上工作。为了更好地理解,