Kotlin Android Coroutines - 暂停功能似乎不在后台运行

Wol*_*ane 0 android kotlin kotlin-coroutines

我觉得我错过了我对以下代码如何工作的理解的一些关键部分:


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)
    }
Run Code Online (Sandbox Code Playgroud)

这是我在运行时得到的错误:

java.lang.IllegalStateException: Cannot access database on the main thread since it may potentially lock the UI for a long period of time.
Run Code Online (Sandbox Code Playgroud)

我不明白为什么网络代码通过改造工作但存储功能失败.我希望有人能告诉我发生了什么事?

有趣的是,如果我用异步{} .await包装db调用它可以工作,这是否意味着协同程序只能调用其他协同程序?

Mar*_*nik 6

协同程序不是在前台或后台运行.它们是关于被挂起的能力,就像本机线程被操作系统暂停一样,但是在你控制这种行为的层面上.

当你说launch(UI) { some code },你告诉Kotlin将"一些代码"作为任务提交给GUI事件循环.它将在GUI线程上运行,直到明确暂停; 唯一的区别是它不会立即运行,因此launch(UI)块下面的下一行代码将在它之前运行.

当你的"某些代码"遇到一个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)
}
Run Code Online (Sandbox Code Playgroud)

我还要补充一点,你最好创建自己的线程池而不是依赖于系统范围CommonPool.有关详细信息,请参阅此主题.