Kotlin Android 协程 - 挂起函数似乎不在后台 运行
Kotlin Android Coroutines - suspend function doesn't seem to run in the background
我觉得我在理解下面这段代码的工作原理时遗漏了一些关键部分:
private fun retrieveAndStore() {
launch(UI) {
val service = retrofit.create(AWSService::class.java)
val response = service.retrieveData().await()
store(data = response)
}
}
private suspend fun store(data: JsonData) {
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app-db").build()
db.appDao().insert(storyData)
}
这是我在 运行:
时得到的错误
java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
我不明白为什么通过改造的网络代码有效,但商店功能失败。我希望有人能告诉我这是怎么回事?
有趣的是,如果我用 async {}.await 包装数据库调用它可以工作,这是否意味着协程只能调用其他协程?
协程与 运行 在前台或后台中的宁无关。它们是关于暂停的能力,就像本机线程被 OS 暂停一样,但在您控制该行为的级别上。
当你说 launch(UI) { some code }
时,你告诉 Kotlin 将 "some code" 作为任务提交给 GUI 事件循环。它将在 GUI 线程上 运行 直到明确挂起;唯一的区别是它不会立即 运行 所以 launch(UI)
块下面的下一行代码将在它之前 运行。
当你的 "some code" 遇到一个 suspendCoroutine
调用时,神奇的部分就出现了:这是它的执行停止的地方,你在传递给 suspendCoroutine
的块中得到一个延续对象。您可以随心所欲地处理该对象,通常将其存储在某个地方,然后稍后再恢复。
通常您看不到 suspendCoroutine
调用,因为它在您正在调用的某些 suspend fun
的实现中,但您可以自由实现自己的调用。
一个这样的库函数是withContext
,它是您解决问题所需要的。它使用您传递给它的块创建另一个协程,将该协程提交到您指定的其他上下文(一个有用的示例是 CommonPool
),然后挂起当前协程直到另一个协程完成。这正是您将阻塞调用转换为可挂起函数所需要的。
在你的情况下,它看起来像这样:
private suspend fun store(data: JsonData) = withContext(CommonPool) {
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app-db").build()
db.appDao().insert(storyData)
}
我还要补充一点,您最好创建自己的线程池,而不是依赖 system-wide CommonPool
。详见。
我觉得我在理解下面这段代码的工作原理时遗漏了一些关键部分:
private fun retrieveAndStore() {
launch(UI) {
val service = retrofit.create(AWSService::class.java)
val response = service.retrieveData().await()
store(data = response)
}
}
private suspend fun store(data: JsonData) {
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app-db").build()
db.appDao().insert(storyData)
}
这是我在 运行:
时得到的错误java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
我不明白为什么通过改造的网络代码有效,但商店功能失败。我希望有人能告诉我这是怎么回事?
有趣的是,如果我用 async {}.await 包装数据库调用它可以工作,这是否意味着协程只能调用其他协程?
协程与 运行 在前台或后台中的宁无关。它们是关于暂停的能力,就像本机线程被 OS 暂停一样,但在您控制该行为的级别上。
当你说 launch(UI) { some code }
时,你告诉 Kotlin 将 "some code" 作为任务提交给 GUI 事件循环。它将在 GUI 线程上 运行 直到明确挂起;唯一的区别是它不会立即 运行 所以 launch(UI)
块下面的下一行代码将在它之前 运行。
当你的 "some code" 遇到一个 suspendCoroutine
调用时,神奇的部分就出现了:这是它的执行停止的地方,你在传递给 suspendCoroutine
的块中得到一个延续对象。您可以随心所欲地处理该对象,通常将其存储在某个地方,然后稍后再恢复。
通常您看不到 suspendCoroutine
调用,因为它在您正在调用的某些 suspend fun
的实现中,但您可以自由实现自己的调用。
一个这样的库函数是withContext
,它是您解决问题所需要的。它使用您传递给它的块创建另一个协程,将该协程提交到您指定的其他上下文(一个有用的示例是 CommonPool
),然后挂起当前协程直到另一个协程完成。这正是您将阻塞调用转换为可挂起函数所需要的。
在你的情况下,它看起来像这样:
private suspend fun store(data: JsonData) = withContext(CommonPool) {
val db = Room.databaseBuilder(applicationContext, AppDatabase::class.java, "app-db").build()
db.appDao().insert(storyData)
}
我还要补充一点,您最好创建自己的线程池,而不是依赖 system-wide CommonPool
。详见