我正在基于从API获取的大量数据制作一个应用程序。在显示实际应用程序之前,我需要这些信息,因此我将负责获取这些信息的代码放在加载屏幕上。我将所有这些信息保存在本地数据库(Room Database)中,这样我就不必在每次启动应用程序时都获取这些信息(因为API没有太大变化),因此我可以实际使用这些信息。
**第一个问题:**当我尝试我的代码时,它会保存对象两次(或更多)(可能是因为协程)。由于我基于是否结束提取取决于发生了多少次保存 * / * 结果数量 *,这会导致Activity认为提取已经完成,停止所有调用。这意味着有些对象没有时间保存。
下面是我的代码(简化):
class FetchFromAPI {
fun fetchObjects(save: (Object) -> Unit) {
//Get number objects from API call
val count = API.getCount()
//Calculate number of pages
val limit = 100
var pageCount = count / limit
if (count % limit != 0) pageCount++
//For each page
for (i in 1..pageCount) {
CoroutineScope(Dispatchers.IO).launch {
//Get page
val page = API.getPage(i)
//Save objects from page
savePage(page, save)
}
}
}
private fun savePage(
page: JSONArray,
save: (Object) -> Unit
) {
//For each object in page
for (i in 0..page.length()) {
CoroutineScope(Dispatchers.IO).launch {
//Get object
val obj = page.getJSONObject(i)
//Save object
saveObject(obj, save)
}
}
}
private fun saveObject(
obj: JSONObject,
save: (Object) -> Unit
) {
//Convert Json to Object
...
save(obj)
}
}
字符串saveObject(obj)
是一个将对象保存在Room Database上并更新提取进度的函数(由Flows控制)。
所以我在谷歌上找到了关于作业和runBlocking{}
的join()
,我想“我不需要进度的确切数字,因为它是用ProgressBar显示的,我正在处理几千个对象,所以如果我能知道我所有的协程何时完成,并基于此而不是进度来调用返回,我很好(即使多个电话浪费了一些资源)。
**第二个问题:**当我尝试使用runBlocking{}
或join()
时,我可以启动一个函数说它已经完成,该函数要么给我一个“应用程序被冻结”错误,要么根本无法启动。
我的代码已更改(简化):
class FetchFromAPI {
fun fetchObjects(save: (Object) -> Unit) {
//Get number objects from API call
val count = API.getCount()
//Calculate number of pages
val limit = 100
var pageCount = count / limit
if (count % limit != 0) pageCount++
runBlocking {
//For each page
for (i in 1..pageCount) {
launch {
//Get page
val page = API.getPage(i)
//Save objects from page
savePage(page, save)
}
}
}
}
private fun savePage(
page: JSONArray,
save: (Object) -> Unit
) {
//For each object in page
runBlocking {
for (i in 0..page.length()) {
launch {
//Get object
val obj = page.getJSONObject(i)
//Save object
saveObject(obj, save)
}
}
}
}
private fun saveObject(
obj: JSONObject,
save: (Object) -> Unit
) {
//Convert Json to Object
...
save(obj)
}
}
fun fetchingFromAPI(onFinished: () -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
fetchObjects(::save)
onFinished()
}
}
型
关于协程,join()
和runBlocking{}
,我有什么不明白的吗?
如果你还需要我的代码告诉我。
1条答案
按热度按时间drkbr07n1#
runBlocking会阻塞你调用它的线程,直到完成它的执行。可能这就是冻结错误的原因,因为你在for循环中阻塞了IO线程,然后你从那里调用了保存回调。我建议你让所有函数挂起,以便能够在没有runBlocking的情况下使用join。当一个函数挂起时,它可以挂起它的执行,并且可以加入其他协程。
字符串
现在你可以像在代码的第二部分那样从协程调用fetchObjects:
型
可能这需要一点调试,但因为我没有你的代码的其余部分,我不能这样做,所以请让我知道,如果有任何错误,或者如果不像你预期的那样工作。
在协程中切换线程的示例:
型