kotlin中协程_android中的kotlin协程以示例方式了解暂停功能和主要安全性

kotlin中协程

When I have started learning about coroutines in Android in order to replace my executors and callbacks, I had a learning curve about how to actually use the coroutines, what runs on the main thread and what actually runs in a background thread. In this post I will demonstrate by example how to use coroutines to do some heavy calculations on a background thread using coroutines and providing main-safety.

当我开始学习Android中的协程以替换执行程序和回调时,我对如何实际使用协程,在主线程上运行以及在后台线程中实际运行有了一条学习曲线。 在这篇文章中,我将通过示例演示如何使用协程在后台线程上使用协程并提供主要安全性来进行一些繁重的计算。

Android开发的第一条规则:不要阻塞主线程! (The first rule of Android development: Don’t block the main thread!)

Starting with coroutines one might think that adding a function the keyword suspend will run the function in a background thread. This is not the case, a suspend function unless stated otherwise, will run on the main thread. What is a suspend function then?

从协程开始,人们可能会认为,添加一个带有关键字suspend的函数将在后台线程中运行该函数。 事实并非如此, 除非另有说明,挂起函数将在主线程上运行 。 那么暂停功能是什么?

From code lab:

从代码实验室:

The keyword suspend is Kotlin’s way of marking a function, or function type, available to coroutines. When a coroutine calls a function marked suspend, instead of blocking until that function returns like a normal function call, it suspends execution until the result is ready then it resumes where it left off with the result. While it’s suspended waiting for a result, it unblocks the thread that it’s running on so other functions or coroutines can run.

关键字suspend是Kotlin标记协程可用的函数或函数类型的方式。 当协程调用标记为暂停的函数时,它不会像正常的函数调用那样阻塞直到该函数返回,而是暂停执行直到结果准备就绪,然后从结果中退回。 在暂停等待结果时,它会解除阻塞正在运行的线程,以便其他函数或协程可以运行。

Let’s make some order in that paragraph:

让我们在该段中进行一些排序:

  • A suspend function will be called from a coroutine

    暂停函数将从协程调用

  • In case the function is a long running task (blocking), it will suspend and let the calling thread resume

    如果函数是长时间运行的任务(阻塞),它将暂停并让调用线程恢复
  • When the blocking function is suspended, other functions on the calling thread can run

    当阻塞函数挂起时,调用线程上的其他函数可以运行
  • When the result of the blocking function is ready then it resumes

    当阻止功能的结果准备就绪时,它将恢复

It all sounds nice, however some clarifications are needed:

听起来不错,但是需要一些说明:

  • Declaring a function suspend does not do anything more then it could do if the function was not suspending.

    声明函数挂起并没有做更多的事情,而如果函数未挂起则可以做。

  • This means adding suspend to a blocking function called from the main thread, will still run on the main thread

    这意味着将暂停添加到从主线程调用的阻塞函数中, 仍将在主线程上运行

  • Thus the blocking function should be run explicitly on a background thread

    因此,阻塞函数应在后台线程上显式运行

那我们该如何解决呢? (How do we solve it then?)

Let’s Consider the following example:

让我们考虑以下示例:

  • MainActivity with two TextViews

    具有两个TextViews的MainActivity
  • First textView will display a calculation of a large number (result from blocking function)

    第一个textView将显示大量计算(阻止功能的结果)
  • The second will display “Hello World!”

    第二个将显示“ Hello World!”。
  • MainViewModel with a function countToOneBillion() that will count to one billion (1,000,000,000) and set the number in MainActivity

    具有函数countToOneBillion()的MainViewModel,该函数将计数为十亿(1,000,000,000),并在MainActivity中设置数字
  • If countToOneBillion() is blocking, “Hello World!” won’t be displayed until countToOneBillion() finishes

    如果countToOneBillion()被阻止,则“ Hello World!” 在countToOneBillion()完​​成之前不会显示
notice the second TextView with text: “Hello World!”
注意带有文本的第二个TextView:“ Hello World!”
The activity updating the text view, Hello World! is default text and won’t be changed
该活动正在更新文本视图,Hello World! 是默认文字,不会更改

the view model:

视图模型:

In this example countToOneBillion() is blocking and will block “Hello World!” from displaying

在此示例中,countToOneBillion()正在阻止并将阻止“ Hello World!”。 从显示

Test case 1:Wrap countToOneBillion() in a coroutine with viewModelScope.launchWe will run the blocking function in the view model scope

测试用例1:将countToOneBillion()与viewModelScope.launch封装在协程中我们将在视图模型范围内运行阻塞函数

Result: “Hello World!” isn’t displayed before the calculation is complete, thus countToOneBillion() is still blocking

结果:“ Hello World!” 在计算完成之前未显示,因此countToOneBillion()仍在阻塞

but why isn’t it enough? from android developer site:

但是为什么还不够呢? 来自android开发者网站:

viewModelScope.launch will start a coroutine in the viewModelScope. This means when the job that we passed to viewModelScope gets canceled, all coroutines in this job/scope will be cancelled. If the user left the Activity before delay returned, this coroutine will automatically be cancelled when onCleared is called upon destruction of the ViewModel.

viewModelScope.launch将在viewModelScope中启动协程。 这意味着当我们传递给viewModelScope的作业被取消时,该作业/范围中的所有协程将被取消。 如果用户在延迟返回之前离开了Activity,则在破坏ViewModel时调用onCleared时,此协程将自动取消。

Since viewModelScope has a default dispatcher of Dispatchers.Main, this coroutine will be launched in the main thread.

由于viewModelScope具有Dispatchers.Main的默认调度程序,因此此协程将在主线程中启动。

This means countToOneBillion() is still blocking, and just running a code in a coroutine or launching the function in the correct scope (viewModelScope) doesn’t run it in the background. countToOneBillion() still runs on the main thread, since it is the default dispatcher which is Dispatchers.Main.

这意味着countToOneBillion()仍然处于阻塞状态,仅在协程中运行代码或在正确的作用域(viewModelScope)中启动函数不会在后台运行它。 countToOneBillion()仍在主线程上运行,因为它是默认的分派器,即Dispatchers.Main。

Test case 2:Add suspend to countToOneBillion(). For this example let’s wrap the calculation with the function: countToOneBillionInternal(), for better readability and for adding the suspend keyword (suspend function can be called from a coroutine scope or from another suspend function)

测试案例2:将暂停添加到countToOneBillion()。 对于此示例,让我们用以下函数包装计算:countToOneBillionInternal(),以提高可读性并添加suspend关键字(可以从协程范围或另一个suspend函数调用suspend函数)

Result:

结果:

  • “Hello World!” isn’t displayed before the calculation is complete, thus countToOneBillion() is still blocking

    “你好,世界!” 在计算完成之前未显示,因此countToOneBillion()仍在阻塞
  • Android studio will grey out the suspend keyword stating this is redundant

    Android Studio会将灰色的suspend关键字变灰,表明这是多余的

  • This means adding suspend doesn’t do anything to the blocking function

    这意味着添加暂停对阻止功能没有任何作用

Why is it still blocking?Running a suspend function, unless specified otherwise will run on Dispatcher.Main which means it will run on main thread, if you want to achieve background work, you will HAVE to specify which Dispatcher should the function run on.

为什么它仍然阻塞? 运行悬挂函数,除非另有说明,否则将在Dispatcher.Main上运行。这意味着它将在主线程上运行,如果要完成后台工作,则必须指定函数应在哪个Dispatcher上运行。

Dispatchers.Main — Use this dispatcher to run a coroutine on the main Android thread. This should be used only for interacting with the UI and performing quick work. Examples include calling suspend functions, running Android UI framework operations, and updating LiveData objects.

Dispatchers.Main :使用此调度程序在Android主线程上运行协程。 这仅应用于与UI交互并执行快速工作。 示例包括调用暂停函数,运行Android UI框架操作以及更新LiveData对象。

Dispatchers.IO — This dispatcher is optimized to perform disk or network I/O outside of the main thread. Examples include using the Room component, reading from or writing to files, and running any network operations.

Dispatchers.IO :此调度程序经过优化,可以在主线程之外执行磁盘或网络I / O。 示例包括使用Room组件 ,读取或写入文件以及运行任何网络操作。

Dispatchers.Default — This dispatcher is optimized to perform CPU-intensive work outside of the main thread. Example use cases include sorting a list and parsing JSON.

Dispatchers.Default —此调度程序经过优化,可以在主线程之外执行CPU密集型工作。 用例示例包括对列表进行排序和解析JSON。

This means we will need to run our function in Dispatchers.Default since we are performing a heavy calculation and not an IO operation. For this we will use the function withContext().

这意味着我们需要在Dispatchers中运行我们的函数。 默认是因为我们要执行繁重的计算而不是IO操作。 为此,我们将使用withContext()函数。

Test case 3:

测试案例3:

add withContext(Dispatechers.Default) to countToOneBillion()

将withContext(Dispatechers.Default)添加到countToOneBillion()

Result:

结果:

  • MainActivity will display “0” for the count TextView (default value)

    MainActivity将为计数TextView显示“ 0”(默认值)
  • MainActivity will display “Hello world!” for the second textView (Nothing is blocking the main thread)

    MainActivity将显示“你好,世界!” 对于第二个textView(什么都没有阻塞主线程)
  • When countToOneBillion() finishes, it will set the correct value to main thread

    当countToOneBillion()完​​成时,它将为主线程设置正确的值
  • We have achieved main-safety

    我们已经实现了主要安全

结论 (Conclusion)

  • Suspend functions will run by default on main thread

    挂起功能将默认在主线程上运行
  • In order to achieve background work and main-safety, it is needed to run blocking functions on a correct dispatcher

    为了实现后台工作和主安全,需要在正确的调度程序上运行阻止功能
  • Available dispatchers for background work are Dispatcher.IO (optimized for IO) and Dispatcher.Default (for long calculations)

    可用于后台工作的调度程序是Dispatcher.IO(针对IO优化)和Dispatcher.Default(用于较长的计算)
  • Some third party libraries (room, retrofit) come with main safety build in and it is enough to use the suspend keyword when using them

    一些第三方库(房间,翻新库)内置了主要安全功能,使用它们时足以使用suspend关键字
  • In order to achieve main-safety in your project, you have to take care of the background work by yourself

    为了在您的项目中实现主要安全,您必须自己照顾后台工作

Extras: gradle configuration

其他:gradle配置

// Coroutines
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2"// ViewModel and LiveData
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0"

翻译自: https://medium.com/@benny.pivnic/kotlin-coroutines-in-android-understanding-suspend-functions-and-main-safety-by-example-6bb0fb7a5ff7

kotlin中协程

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值