Kotlin 协程协程取消 ③ ( finally 释放协程资源 | 使用 use 函数执行 Closeable 对象释放资源操作 | 构造无法取消的协程任务 | 构造超时取消的协程任务 )
Posted 韩曙亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin 协程协程取消 ③ ( finally 释放协程资源 | 使用 use 函数执行 Closeable 对象释放资源操作 | 构造无法取消的协程任务 | 构造超时取消的协程任务 )相关的知识,希望对你有一定的参考价值。
文章目录
- 一、释放协程资源
- 二、使用 use 函数执行 Closeable 对象释放资源操作
- 三、使用 withContext(NonCancellable) 构造无法取消的协程任务
- 四、使用 withTimeoutOrNull 函数构造超时取消的协程任务
一、释放协程资源
如果 协程中途取消 , 期间需要 释放协程占有的资源 ;
如果执行的协程任务中 , 需要 执行 关闭文件 , 输入输出流 等操作 , 推荐使用 try…catch…finally 代码块 , 在 finally 代码块中的代码 , 即使是协程取消时 , 也会执行 ;
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.intrinsics.COROUTINE_SUSPENDED
import kotlin.coroutines.intrinsics.intercepted
import kotlin.coroutines.intrinsics.suspendCoroutineUninterceptedOrReturn
class MainActivity : AppCompatActivity()
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
// 创建协程作用域
val coroutineScope = CoroutineScope(Dispatchers.Default)
val job1 = coroutineScope.launch
try
Log.i(TAG, "协程任务执行开始")
var i = 0
while (i < 10000000)
yield()
var j = i + 1
i++
if(j == 10000000)
Log.i(TAG, "最后一次循环 : j = $j")
Log.i(TAG, "协程任务执行完毕")
catch (e: Exception)
Log.i(TAG, "协程抛出异常")
e.printStackTrace()
finally
Log.i(TAG, "释放协程占用的资源")
// 100ms 后取消协程作用域
delay(10)
Log.i(TAG, "取消协程任务")
// 取消协程任务
job1.cancelAndJoin()
Log.i(TAG, "退出协程作用域")
执行结果 : 即使是取消协程任务后 , 在协程抛出 JobCancellationException 异常后 , finally 中的代码在最后也被执行了 ;
22:06:06.455 I 协程任务执行开始
22:06:06.504 I 取消协程任务
22:06:06.508 I 协程抛出异常
22:06:06.509 W kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutineCancelling@bc6a601
22:06:06.509 I 释放协程占用的资源
22:06:06.510 I 退出协程作用域
二、使用 use 函数执行 Closeable 对象释放资源操作
使用 use 函数 可以在 程序结束时 , 执行实现了 Closeable 对象的的 close 方法 , 该操作适合文件对象关闭文件使用 ;
use 函数原型如下 :
/**
* 在这个资源上执行给定的[block]函数,然后正确关闭它,不管是否异常
* 是否被抛出。
*
* @param block 处理这个[Closeable]资源的函数。
* @return t在这个资源上调用[block]函数的结果。
*/
@InlineOnly
@RequireKotlin("1.2", versionKind = RequireKotlinVersionKind.COMPILER_VERSION, message = "Requires newer compiler version to be inlined correctly.")
public inline fun <T : Closeable?, R> T.use(block: (T) -> R): R
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import java.io.BufferedReader
import java.io.FileReader
class MainActivity : AppCompatActivity()
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
// 创建协程作用域
val coroutineScope = CoroutineScope(Dispatchers.Default)
val job1 = coroutineScope.launch
// Context.getFilesDir() 为 /data/data/com.android.example/files
BufferedReader(FileReader("$filesDir/name.txt")).use
Log.i(TAG, "协程任务执行开始")
delay(20000)
Log.i(TAG, "协程任务执行完毕")
// 100ms 后取消协程作用域
delay(10)
Log.i(TAG, "取消协程任务")
// 取消协程任务
job1.cancelAndJoin()
Log.i(TAG, "退出协程作用域")
在上述 BufferedReader 的 close 函数中 , 自动关闭了输入流 ;
public class BufferedReader extends Reader
public void close() throws IOException
synchronized (lock)
if (in == null)
return;
try
in.close();
finally
in = null;
cb = null;
三、使用 withContext(NonCancellable) 构造无法取消的协程任务
如果在 finally 中需要使用 suspend 挂起函数 , 则 挂起函数以及之后的代码将不会被执行 ;
如下代码 :
val job1 = coroutineScope.launch
try
Log.i(TAG, "协程任务执行开始")
finally
Log.i(TAG, "释放协程占用的资源")
delay(1000)
Log.i(TAG, "释放协程占用的资源完毕")
如果在协程取消后 , finally 代码块的代码肯定会执行 , 但是如果 finally 中 delay 挂起函数以及之后的代码将不会被执行 ;
使用 withContext(NonCancellable)
代码块 , 可以构造一个无法取消的协程任务 , 这样可以避免 finally 中的代码无法完全执行 ;
withContext(NonCancellable)
Log.i(TAG, "释放协程占用的资源")
delay(1000)
Log.i(TAG, "释放协程占用的资源完毕")
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.*
import java.io.BufferedReader
import java.io.FileReader
class MainActivity : AppCompatActivity()
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
// 创建协程作用域
val coroutineScope = CoroutineScope(Dispatchers.Default)
val job1 = coroutineScope.launch
try
Log.i(TAG, "协程任务执行开始")
var i = 0
while (i < 10000000)
yield()
var j = i + 1
i++
if(j == 10000000)
Log.i(TAG, "最后一次循环 : j = $j")
Log.i(TAG, "协程任务执行完毕")
catch (e: Exception)
Log.i(TAG, "协程抛出异常")
e.printStackTrace()
finally
withContext(NonCancellable)
Log.i(TAG, "释放协程占用的资源")
delay(1000)
Log.i(TAG, "释放协程占用的资源完毕")
// 100ms 后取消协程作用域
delay(10)
Log.i(TAG, "取消协程任务")
// 取消协程任务
job1.cancelAndJoin()
Log.i(TAG, "退出协程作用域")
执行结果 : finally 代码块中存在挂起函数 , 但是整个代码块被 withContext(NonCancellable) 代码块包裹 , 所有的代码执行完毕 ;
23:12:31.014 I 协程任务执行开始
23:12:31.029 I 取消协程任务
23:12:31.033 I 协程抛出异常
23:12:31.034 W kotlinx.coroutines.JobCancellationException: StandaloneCoroutine was cancelled; job=StandaloneCoroutineCancelling@bc6a601
23:12:31.039 I 释放协程占用的资源
23:12:31.135 I remove existing package for update: kim.hsl.coroutine
23:12:32.091 I 释放协程占用的资源完毕
23:12:32.093 I 退出协程作用域
四、使用 withTimeoutOrNull 函数构造超时取消的协程任务
使用 withTimeout
函数 , 可以构造超时取消的协程任务 , 在下面的代码中 , 构造的协程任务 , 超时 1000 ms 就会自动取消 , 如果超时则报 kotlinx.coroutines.TimeoutCancellationException
异常信息 ;
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeout
class MainActivity : AppCompatActivity()
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
// 构造超时取消的协程任务
withTimeout(1000)
// 超过 1000 ms 没有取消的任务
Log.i(TAG, "协程任务执行开始")
delay(12000)
Log.i(TAG, "协程任务执行结束")
执行结果 :
23:28:08.849 I 协程任务执行开始
23:28:09.888 D Shutting down VM
23:28:09.894 E FATAL EXCEPTION: main
Process: kim.hsl.coroutine, PID: 8050
java.lang.RuntimeException: Unable to start activity ComponentInfokim.hsl.coroutine/kim.hsl.coroutine.MainActivity: kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2951)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: kotlinx.coroutines.TimeoutCancellationException: Timed out waiting for 1000 ms
at kotlinx.coroutines.TimeoutKt.TimeoutCancellationException(Timeout.kt:186)
at kotlinx.coroutines.TimeoutCoroutine.run(Timeout.kt:156)
at kotlinx.coroutines.EventLoopImplBase$DelayedRunnableTask.run(EventLoop.common.kt:507)
at kotlinx.coroutines.EventLoopImplBase.processNextEvent(EventLoop.common.kt:277)
at kotlinx.coroutines.DefaultExecutor.run(DefaultExecutor.kt:69)
at java.lang.Thread.run(Thread.java:764)
如果 需要 构造一个超时取消协程 返回一个 返回值 , 则使用 withTimeoutOrNull
函数进行构造 , 如果顺利执行 , 则按照正常返回值返回 , 如果执行超时 , 则直接返回 null ;
代码示例 :
package kim.hsl.coroutine
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withTimeoutOrNull
class MainActivity : AppCompatActivity()
val TAG = "MainActivity"
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
runBlocking
// 构造超时取消的协程任务
val result = withTimeoutOrNull(1000)
// 超过 1000 ms 没有取消的任务
Log.i(TAG, "协程任务执行开始")
delay(12000)
Log.i(TAG, "协程任务执行结束")
// 执行完毕后的返回值
// 如果超时则返回 null
"(withTimeoutOrNull 返回值)"
Log.i(TAG, "上述协程任务的返回值为 $result")
执行结果 :
23:34:35.778 I 协程任务执行开始
23:34:36.794 I 上述协程任务的返回值为 null
以上是关于Kotlin 协程协程取消 ③ ( finally 释放协程资源 | 使用 use 函数执行 Closeable 对象释放资源操作 | 构造无法取消的协程任务 | 构造超时取消的协程任务 )的主要内容,如果未能解决你的问题,请参考以下文章
Kotlin 协程协程底层实现 ③ ( 结构化并发 | MainScope 作用域 | 取消协程作用域 | Activity 实现 CoroutineScope 协程作用域接口 )
Kotlin 协程协程底层实现 ③ ( 结构化并发 | MainScope 作用域 | 取消协程作用域 | Activity 实现 CoroutineScope 协程作用域接口 )
Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )
Kotlin 协程协程异常处理 ③ ( 协程异常处理器 CoroutineExceptionHandler 捕获异常 | 验证 CoroutineScope 协程的异常捕捉示例 )