在 Android 上启动协程的正确方法是什么?
What is the proper way to launch a coroutine on Android?
我正在尝试弄清楚如何启动协程。我想让它依次调用两个挂起函数。
我阅读的第一个文档说要这样做:
class Something {
fun initialize() {
launch {
suspendFun1()
suspendFun2()
}
}
但是 Android Studio 没有找到 launch
方法。然后我了解到 official coroutine docs 建议使用 GlobalScope.launch
:
class Something {
fun initialize() {
GlobalScope.launch {
suspendFun1()
suspendFun2()
}
}
但后来我在 中读到您不应该使用 GlobalScope.launch
。
所以我找到另一个 blog post 解释说我需要一个 CoroutineScope 来调用 launch
。但它没有解释如何构建一个。
然后我发现这个 blog post 解释了如何通过在 class 上实现 CoroutineScope 来为 Activity 和 ViewModels 构建一个:
class Something : CoroutineScope {
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
fun initialize() {
job = Job()
launch {
suspendFun1()
suspendFun2()
}
}
然后我读到这个 blog post 说我不应该实施 CoroutineScope
class Something {
protected val scope = CoroutineScope(
Job() + Dispatchers.Main
)
fun initialize() {
scope.launch {
suspendFun1()
suspendFun2()
}
}
但我不确定我是否理解 Job() + Dispatchers.Main
的含义,因为这似乎也有效:
class Something {
protected val scope = CoroutineScope(Dispatchers.Main)
fun initialize() {
scope.launch {
suspendFun1()
suspendFun2()
}
}
有人可以简单地向我解释一下执行此操作的最佳方法吗?是不是和上面一样我确定已经有人问过这个问题,所以如果这是一个重复的问题,我深表歉意。但如您所见,对此没有明确的答案。我想听听您的意见。
最后两个解决方案没问题。 CoroutineScope(context: CoroutineContext)
如果您传递一个没有上下文的上下文,则会创建一个空的 Job
。
指定 job + dispatcher
只是构建它的更明确的方式,因为在某些情况下您可能希望使用 SupervisorJob
来防止在其中一个子作业失败时取消整个范围。
至于在 Activities
和 ViewModels
中构建作用域,您可以通过导入 KTX 片段模块使用 KTX 库中内置的作用域,而不是声明您自己的作用域:
// add to your apps dependencies
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02'
现在,在您的 Activity
和 Fragment
中,您可以使用 lifecycleScope
and inside ViewModel
a viewModelScope
,它们是由 SupervisorJob + Dispatchers.Main.immediate
支持的范围,并且在它们各自的生命周期被破坏时自动取消。
我有Coroutines.kt
object Coroutines {
fun main(work: suspend (() -> Unit)) {
CoroutineScope(Dispatchers.Main).launch {
work()
}
}
}
我通过调用 Couroutines.main
在我的视图模型中使用它
我处于非常相似的困境中,OP 出色地捕捉到了新手对各种资源的困惑。就我而言,除了创建自己的 CoroutineScope
之外,我别无选择,因为我需要一个 long-运行 作用域,它的生命周期与应用程序进程一样长(它使用挂起函数处理与 Retrofit 的交互) , 它位于 Android 库中,无法访问 ViewModel
和其他 LifecycleOwner
项。
/**
* A [CoroutineScope] for HTTP cloud implementations to launch coroutines in. A coroutine
* dispatcher is not specified because Retrofit will switch to its own custom dispatcher
* when performing suspending network calls.
*/
internal val networkingScope = CoroutineScope(SupervisorJob())
对于那些只是在寻找一种简单的方法来启动带有 Android Activity 的协程的人来说,这里有一个快速简单的方法(但你仍然应该研究学习如何正确使用协程,即使用 ViewModels 和 CoroutineScope)
- 将 androidx.lifecycle 添加到您的 app-level build.gradle:
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"
(查看上面的 Link 了解当前版本)
- 在Activity中,使用Activity-providedLifecycleScope启动协程(主要是pseudo-code):
import androidx.lifecycle.lifecycleScope
class YourActivity {
override fun onCreate() {
lifeCycleScope.launch {
// do your Coroutine Stuff here, i.e. call a suspend fun:
coroutineFunction()
}
}
suspend fun coroutineFunction() {
// Use a different CoroutineScope, etc
CoroutineScope(Dispatchers.IO).launch {
// do some long running operation or something
}
}
}
我正在尝试弄清楚如何启动协程。我想让它依次调用两个挂起函数。
我阅读的第一个文档说要这样做:
class Something {
fun initialize() {
launch {
suspendFun1()
suspendFun2()
}
}
但是 Android Studio 没有找到 launch
方法。然后我了解到 official coroutine docs 建议使用 GlobalScope.launch
:
class Something {
fun initialize() {
GlobalScope.launch {
suspendFun1()
suspendFun2()
}
}
但后来我在 GlobalScope.launch
。
所以我找到另一个 blog post 解释说我需要一个 CoroutineScope 来调用 launch
。但它没有解释如何构建一个。
然后我发现这个 blog post 解释了如何通过在 class 上实现 CoroutineScope 来为 Activity 和 ViewModels 构建一个:
class Something : CoroutineScope {
private lateinit var job: Job
override val coroutineContext: CoroutineContext
get() = job + Dispatchers.Main
fun initialize() {
job = Job()
launch {
suspendFun1()
suspendFun2()
}
}
然后我读到这个 blog post 说我不应该实施 CoroutineScope
class Something {
protected val scope = CoroutineScope(
Job() + Dispatchers.Main
)
fun initialize() {
scope.launch {
suspendFun1()
suspendFun2()
}
}
但我不确定我是否理解 Job() + Dispatchers.Main
的含义,因为这似乎也有效:
class Something {
protected val scope = CoroutineScope(Dispatchers.Main)
fun initialize() {
scope.launch {
suspendFun1()
suspendFun2()
}
}
有人可以简单地向我解释一下执行此操作的最佳方法吗?是不是和上面一样我确定已经有人问过这个问题,所以如果这是一个重复的问题,我深表歉意。但如您所见,对此没有明确的答案。我想听听您的意见。
最后两个解决方案没问题。 CoroutineScope(context: CoroutineContext)
如果您传递一个没有上下文的上下文,则会创建一个空的 Job
。
指定 job + dispatcher
只是构建它的更明确的方式,因为在某些情况下您可能希望使用 SupervisorJob
来防止在其中一个子作业失败时取消整个范围。
至于在 Activities
和 ViewModels
中构建作用域,您可以通过导入 KTX 片段模块使用 KTX 库中内置的作用域,而不是声明您自己的作用域:
// add to your apps dependencies
implementation 'androidx.fragment:fragment-ktx:1.2.0-rc02'
现在,在您的 Activity
和 Fragment
中,您可以使用 lifecycleScope
and inside ViewModel
a viewModelScope
,它们是由 SupervisorJob + Dispatchers.Main.immediate
支持的范围,并且在它们各自的生命周期被破坏时自动取消。
我有Coroutines.kt
object Coroutines {
fun main(work: suspend (() -> Unit)) {
CoroutineScope(Dispatchers.Main).launch {
work()
}
}
}
我通过调用 Couroutines.main
在我的视图模型中使用它我处于非常相似的困境中,OP 出色地捕捉到了新手对各种资源的困惑。就我而言,除了创建自己的 CoroutineScope
之外,我别无选择,因为我需要一个 long-运行 作用域,它的生命周期与应用程序进程一样长(它使用挂起函数处理与 Retrofit 的交互) , 它位于 Android 库中,无法访问 ViewModel
和其他 LifecycleOwner
项。
/**
* A [CoroutineScope] for HTTP cloud implementations to launch coroutines in. A coroutine
* dispatcher is not specified because Retrofit will switch to its own custom dispatcher
* when performing suspending network calls.
*/
internal val networkingScope = CoroutineScope(SupervisorJob())
对于那些只是在寻找一种简单的方法来启动带有 Android Activity 的协程的人来说,这里有一个快速简单的方法(但你仍然应该研究学习如何正确使用协程,即使用 ViewModels 和 CoroutineScope)
- 将 androidx.lifecycle 添加到您的 app-level build.gradle:
implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.4.1"
(查看上面的 Link 了解当前版本)
- 在Activity中,使用Activity-providedLifecycleScope启动协程(主要是pseudo-code):
import androidx.lifecycle.lifecycleScope
class YourActivity {
override fun onCreate() {
lifeCycleScope.launch {
// do your Coroutine Stuff here, i.e. call a suspend fun:
coroutineFunction()
}
}
suspend fun coroutineFunction() {
// Use a different CoroutineScope, etc
CoroutineScope(Dispatchers.IO).launch {
// do some long running operation or something
}
}
}