先决条件:
- Android上的Kotlin协程
- Kotlin协程中的调度程序
- Kotlin协程中的挂起函数
在本文中,将讨论以下主题,例如协程中的工作,如何等待协程以及如何取消协程。每当启动新的协程时,它将返回工作。返回的作业可以在许多地方使用,例如可以用来等待协程做一些工作,或者可以用来取消协程。该作业可用于调用许多功能,例如用于等待协程的join()方法和用于取消协程的执行的cancel()方法。
工作的定义
根据官方文档,作业的定义如下:
A Job is a cancellable thing with a life-cycle that culminates in its completion.Coroutine job is created with launch coroutine builder. It runs a specified block of code and completes on completion of this block.
如何获得工作?
正如讨论的那样,当启动新的协程时将返回作业,因此现在让我们以编程方式查看如何返回作业以及如何使用该作业。
Kotlin
// sample koltin program in kotlin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// A job is returned
val job = GlobalScope.launch(Dispatchers.Default) {
}
}
}
Kotlin
// sample kotlin program for demonstrating job.join method
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// creating/launching a coroutine will return the job
val job = GlobalScope.launch(Dispatchers.Default) {
repeat(5)
{
Log.d(TAG, "Coroutines is still working")
// delay the coroutine by 1sec
delay(1000)
}
}
runBlocking {
// waiting for the coroutine to finish it's work
job.join()
Log.d(TAG, "Main Thread is Running")
}
}
}
Kotlin
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
repeat(5)
{
Log.d(TAG, "Coroutines is still working")
delay(1000)
}
}
runBlocking {
// dealaynig the coroutine by 2sec
delay(2000)
// canceling/stopping the coroutine
job.cancel()
Log.d(TAG, "Main Thread is Running")
}
}
}
Kotlin
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG,"Starting the long calculation...")
// running the loop from 30 to 40
for(i in 30..40)
{
Log.d(TAG, "Result for i =$i : ${fib(i)}")
}
Log.d(TAG, "Ending the long calculation...")
}
runBlocking {
delay(2000)
job.cancel()
Log.d(TAG, "Main Thread is Running")
}
}
// fibonacci function
fun fib(n:Int):Long
{
return if(n==0) 0
else if(n==1) 1
else fib(n-1) + fib(n-2)
}
}
Kotlin
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG, "Starting the long calculation...")
for(i in 30..40)
{
// using isActive fuctionality to check whether the
// coroutine is acitve or not
if(isActive)
Log.d(TAG, "Result for i =$i : ${fib(i)}")
}
Log.d(TAG, "Ending the long calculation...")
}
runBlocking {
delay(2000)
job.cancel()
Log.d(TAG, "Main Thread is Running")
}
}
fun fib(n:Int):Long
{
return if(n==0) 0
else if(n==1) 1
else fib(n-1) + fib(n-2)
}
}
Kotlin
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG, "Starting the long calculation...")
// using withTimeOut function
// which runs the coroutine for 3sec
withTimeout(3000L)
{
for(i in 30..40)
{
if(isActive)
Log.d(TAG, "Result for i =$i : ${fib(i)}")
}
}
Log.d(TAG, "Ending the long calculation...")
}
}
fun fib(n:Int):Long
{
return if(n==0) 0
else if(n==1) 1
else fib(n-1) + fib(n-2)
}
}
使用工作可以完成的事情
协同程序可以通过作业界面上可用的功能进行控制。作业界面提供的许多功能如下:
- 开始()
- 加入()
- 取消()
join()方法
join()函数是一个挂起函数,即可以从协程或另一个挂起函数。 Job会阻塞所有线程,直到在其中编写协程或上下文完成其工作为止。仅当协程完成时,才会执行join()函数之后的行。让我们以一个示例来演示join()函数。
科特林
// sample kotlin program for demonstrating job.join method
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// creating/launching a coroutine will return the job
val job = GlobalScope.launch(Dispatchers.Default) {
repeat(5)
{
Log.d(TAG, "Coroutines is still working")
// delay the coroutine by 1sec
delay(1000)
}
}
runBlocking {
// waiting for the coroutine to finish it's work
job.join()
Log.d(TAG, "Main Thread is Running")
}
}
}
日志输出如下:
时间戳记由椭圆形圆圈表示
可以看出,直到正在运行的协程完成其工作,才允许执行Log语句,并且它可能仅由于join()方法而发生。
cancel()方法
cancel()方法用于取消协程,而无需等待它完成工作。可以说,它与join方法正好相反,在某种意义上, join()方法等待协程完成其全部工作并阻塞所有其他线程,而cancel()方法遇到这种情况则杀死了该方法。协程,即停止协程。让我们以一个示例来说明cancel()函数的工作原理。
科特林
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
repeat(5)
{
Log.d(TAG, "Coroutines is still working")
delay(1000)
}
}
runBlocking {
// dealaynig the coroutine by 2sec
delay(2000)
// canceling/stopping the coroutine
job.cancel()
Log.d(TAG, "Main Thread is Running")
}
}
}
日志输出如下:
时间戳记由椭圆形圆圈表示
如上例所示,取消协程并不总是那么容易。人们应该记住,当某人使用cancel()方法时,协程应该意识到会遇到cancel方法,即,可能会遇到cancel方法而coroutine仍在运行。简而言之,需要有足够的时间告诉协程它已被取消。重复函数的delay()函数确保协程有足够的时间准备。让我们来看一个例子,尝试理解以下段落:
科特林
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG,"Starting the long calculation...")
// running the loop from 30 to 40
for(i in 30..40)
{
Log.d(TAG, "Result for i =$i : ${fib(i)}")
}
Log.d(TAG, "Ending the long calculation...")
}
runBlocking {
delay(2000)
job.cancel()
Log.d(TAG, "Main Thread is Running")
}
}
// fibonacci function
fun fib(n:Int):Long
{
return if(n==0) 0
else if(n==1) 1
else fib(n-1) + fib(n-2)
}
}
日志输出如下:
时间戳记由椭圆形圆圈表示
可以看出,即使在遇到cancel()方法之后,我们的协程也会继续计算数字的斐波那契结果。之所以如此,是因为我们的协程非常忙于进行计算,以至于没有时间取消自身。暂挂那里没有暂挂函数(例如delay() ),我们没有足够的时间告诉协程它已被取消。因此,我们必须手动检查协程是否已取消。可以使用isActive来检查协程是否处于活动状态。
科特林
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG, "Starting the long calculation...")
for(i in 30..40)
{
// using isActive fuctionality to check whether the
// coroutine is acitve or not
if(isActive)
Log.d(TAG, "Result for i =$i : ${fib(i)}")
}
Log.d(TAG, "Ending the long calculation...")
}
runBlocking {
delay(2000)
job.cancel()
Log.d(TAG, "Main Thread is Running")
}
}
fun fib(n:Int):Long
{
return if(n==0) 0
else if(n==1) 1
else fib(n-1) + fib(n-2)
}
}
日志输出如下:
时间戳记由椭圆形圆圈表示
可以看出,与未使用isActive相比,使用isActive增强了协程对其取消的了解,并且在取消后协程的执行非常少。
withTimeOut()
Kotlin协程为上述问题提供了一个很好的解决方案,即协程将自动取消,并且在经过一定时间后将不做任何进一步的计算,而withTimeOut()会这样做。无需使用runBlocking()函数手动取消协程。让我们看看withTimeOut()函数如何工作:
科特林
// sample kotlin program
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import kotlinx.coroutines.*
class MainActivity : AppCompatActivity() {
val TAG:String = "Main Activity"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val job = GlobalScope.launch(Dispatchers.Default) {
Log.d(TAG, "Starting the long calculation...")
// using withTimeOut function
// which runs the coroutine for 3sec
withTimeout(3000L)
{
for(i in 30..40)
{
if(isActive)
Log.d(TAG, "Result for i =$i : ${fib(i)}")
}
}
Log.d(TAG, "Ending the long calculation...")
}
}
fun fib(n:Int):Long
{
return if(n==0) 0
else if(n==1) 1
else fib(n-1) + fib(n-2)
}
}
日志输出如下:
时间戳由椭圆形圆圈显示