📜  Kotlin协程中的工作,等待,取消

📅  最后修改于: 2021-05-13 16:48:22             🧑  作者: Mango

先决条件:

  • Android上的Kotlin协程
  • Kotlin协程中的调度程序
  • Kotlin协程中的挂起函数

在本文中,将讨论以下主题,例如协程中的工作,如何等待协程以及如何取消协程。每当启动新的协程时,它将返回工作。返回的作业可以在许多地方使用,例如可以用来等待协程做一些工作,或者可以用来取消协程。该作业可用于调用许多功能,例如用于等待协程的join()方法和用于取消协程的执行的cancel()方法。

工作的定义

根据官方文档,作业的定义如下:

如何获得工作?

正如讨论的那样,当启动新的协程时将返回作业,因此现在让我们以编程方式查看如何返回作业以及如何使用该作业。

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)
    }
}

日志输出如下:

时间戳由椭圆形圆圈显示

日志输出

想要一个节奏更快,更具竞争性的环境来学习Android的基础知识吗?
单击此处,前往由我们的专家精心策划的指南,以使您立即做好行业准备!