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调用它可以工作,这是否意味着协同程序只能调用其他协同程序?
协同程序不是在前台或后台运行.它们是关于被挂起的能力,就像本机线程被操作系统暂停一样,但是在你控制这种行为的层面上.
当你说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.有关详细信息,请参阅此主题.