挂起(suspend)函数是所有协程的核心。 挂起函数可以执行长时间运行的操作并等待它完成而不会阻塞主线程。
挂起函数的语法与常规函数的语法类似,不同之处在于添加了suspend关键字。 它可以接受一个参数并有一个返回类型。 但是,挂起函数只能由另一个挂起函数或在协程内调用。
suspend fun backgroundTask(param: Int): Int { // long running operation }
在背后,编译器将挂起函数转换为另一个没有挂起关键字的函数,该函数接受一个类型为 Continuation<T>
的附加参数。 例如,上面的函数将由编译器转换为:
fun backgroundTask(param: Int, callback: Continuation<Int>): Int { // long running operation }
withContext
的作用就是指定切换的线程,比如:suspend fun suspendingGetImage(id: String) = withContext(Dispatchers.IO)
。如果你的某个函数比较耗时,也就是要等的操作,那就把它写成 suspend 函数。这就是原则。
耗时操作一般分为两类:I/O 操作和 CPU 计算工作。比如文件的读写、网络交互、图片的模糊处理,都是耗时的,通通可以把它们写进 suspend 函数里。
另外这个「耗时」还有一种特殊情况,就是这件事本身做起来并不慢,但它需要等待,比如 5 秒钟之后再做这个操作。这种也是 suspend 函数的应用场景。
假设 postItem
由三个有依赖关系的异步子任务组成: requestToken
,createPost
和 processPost
,这三个函数都是基于回调的 API:
// 三个基于回调的 API fun requestToken(block: (String) -> Unit) fun createPost( token: String, item: Item, block: (Post) -> Unit) ) fun processPost(post: Post) fun postItem(item: Item) { requestToken { token -> createPost(token, item) { post -> processPost(post) } } }
可以看到基于回调的 API 很容易造成大量缩进。如果代码中再加上一些条件、循环的逻辑,那么代码可读性会大大降低。Kotlin 的 suspend 关键字可以帮助我们消除回调,用同步的写法写异步:
suspend fun requestToken(): String suspend fun createPost(token: String, item: Item): Post suspend fun processPost(post) suspend fun postItem(item: Item) { val token = 🏹 requestToken() val post = 🏹 createPost(token, item) 🏹 processPost(post) }
由于 createPost 这些方法实际上是耗时的 IO 异步操作,需要等到拿到返回值才能执行后面的逻辑,但我们又不希望阻塞当前线程(通常是主线程),因此最终必须实现某种消息传递的机制,让后台线程做完耗时操作以后把结果传给主线程。
一个基本的使用方式:
suspend fun getUserInfo(): String { withContext(Dispatchers.IO) { delay(1000L) } return "BoyCoder" }
在 Room 里面会经常用到:
@Dao interface RegisterDatabaseDao { @Insert suspend fun insert(register: RegisterEntity) //@Delete //suspend fun deleteSubscriber(register: RegisterEntity):Int @Query("SELECT * FROM Register_users_table ORDER BY userId DESC") fun getAllUsers(): LiveData<List<RegisterEntity>> @Query("DELETE FROM Register_users_table") suspend fun deleteAll(): Int @Query("SELECT * FROM Register_users_table WHERE user_name LIKE :userName") suspend fun getUsername(userName: String): RegisterEntity? }
最后这个例子可以直接在 Kotlin Playground 上跑。
import kotlinx.coroutines.* import java.util.* import java.time.LocalDate import java.time.format.DateTimeFormatter import java.time.Period import java.text.SimpleDateFormat import java.lang.Thread var dateTimeNow = "" @OptIn(DelicateCoroutinesApi::class) fun main() = runBlocking{ dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("code start: ${dateTimeNow}") launch { dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("1 code start: ${dateTimeNow}") delay(2000L) dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("2 Task from runBlocking: ${dateTimeNow}") } coroutineScope { // Creates a new coroutine scope dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("3 coroutineScope created: ${dateTimeNow}") val job = launch { dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("4 coroutineScope job starts: ${dateTimeNow}") val one = doSomethingUsefulOne() val two = doSomethingUsefulTwo() dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("5 coroutineScope job ends: ${dateTimeNow}") } val job2 = launch { dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("11 coroutineScope job2 starts: ${dateTimeNow}") } delay(1000L) dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("6 Task from first coroutine scope: ${dateTimeNow}") // Printed before initial launch //job.cancel() // This cancels nested launch's execution } dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("code end: ${dateTimeNow}") } fun dateAsString( dateInMillis: Long, format: String = "yyyyMMdd HH:mm:ss", locale: Locale = Locale.getDefault() ): String { val date = Date(dateInMillis) val formatter = SimpleDateFormat(format, locale) return formatter.format(date) } suspend fun doSomethingUsefulOne(): Int { dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("7 第一个挂起函数开始: ${dateTimeNow}") delay(1000L) // 假设我们在这里做了某些有用的工作 dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("8 第一个挂起函数结束: ${dateTimeNow}") return 1 } suspend fun doSomethingUsefulTwo(): Int { dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("9 第二个挂起函数开始: ${dateTimeNow}") delay(2000L) // 假设我们在这里也做了某些有用的工作 dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("10 第二个挂起函数结束: ${dateTimeNow}") coroutineScope { val job = launch { doSomethingUsefulThree() doSomethingUsefulFour() } } return 2 } suspend fun doSomethingUsefulThree(): Int { dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("9 第三个挂起函数开始: ${dateTimeNow}") delay(3000L) // 假设我们在这里也做了某些有用的工作 dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("10 第三个挂起函数结束: ${dateTimeNow}") return 3 } suspend fun doSomethingUsefulFour(): Int { dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("9 第四个挂起函数开始: ${dateTimeNow}") delay(3000L) // 假设我们在这里也做了某些有用的工作 dateTimeNow = dateAsString(Calendar.getInstance().time.time) println("10 第四个挂起函数结束: ${dateTimeNow}") return 4 }
打印的结果如下:
code start: 20221009 03:15:55
3 coroutineScope created: 20221009 03:15:55
1 code start: 20221009 03:15:55
4 coroutineScope job starts: 20221009 03:15:55
11 coroutineScope job2 starts: 20221009 03:15:55
7 第一个挂起函数开始: 20221009 03:15:55
6 Task from first coroutine scope: 20221009 03:15:56
8 第一个挂起函数结束: 20221009 03:15:56
9 第二个挂起函数开始: 20221009 03:15:56
2 Task from runBlocking: 20221009 03:15:57
10 第二个挂起函数结束: 20221009 03:15:58
9 第三个挂起函数开始: 20221009 03:15:58
10 第三个挂起函数结束: 20221009 03:16:01
9 第四个挂起函数开始: 20221009 03:16:01
10 第四个挂起函数结束: 20221009 03:16:04
5 coroutineScope job ends: 20221009 03:16:04
code end: 20221009 03:16:04
有几点需要说明:
到此这篇关于Kotlin Suspend挂起函数的使用详解的文章就介绍到这了,更多相关Kotlin Suspend挂起函数内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
编程 | 2023-02-24 21:36
编程 | 2023-02-21 12:51
编程 | 2023-02-21 12:47
编程 | 2023-02-21 00:15
编程 | 2023-02-21 00:08
编程 | 2023-02-20 21:46
编程 | 2023-02-20 21:42
编程 | 2023-02-20 21:36
编程 | 2023-02-20 21:32
编程 | 2023-02-20 18:12
网友评论