我正在根据从 API 获得的大量数据制作一个应用程序。在显示实际应用程序之前我需要这些信息,因此我将负责获取这些信息的代码放在加载屏幕上。我将所有信息保存在本地数据库(房间数据库)中,这样我就不必每次启动应用程序时都获取它(因为 API 变化不大),所以我实际上可以使用这些信息。
第一个问题:当我尝试我的代码时,它保存对象两次(或更多)(可能是因为协程)。由于我根据“发生了多少次保存”/“结果数量”来确定提取是否结束,这会导致活动认为提取已完成,从而停止所有调用。这意味着某些对象没有时间保存。
这是我的代码(简化):
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 数据库上并更新获取进度的函数(由 Flows 控制)。
所以我在谷歌上搜索并发现了有关工作的
join()
和runBlocking{}
,我想“我不需要进度的确切数字,因为它是用进度条显示的,而且我正在处理数千个对象,因此,如果我可以知道所有协程何时完成,并以此为基础进行返回调用而不是进度,那么我就很好(即使多个调用浪费了一些资源)”。
第二个问题:当我尝试使用
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{}
有什么我不明白的地方吗?
如果您需要我的代码中的其他内容,请告诉我。runBlocking 会阻塞您调用它的线程,直到完成它的执行。 可能这就是冻结错误的原因,因为您在 for 循环中阻塞了 IO 线程,并从那里调用了保存回调。 我建议您暂停所有函数,以便能够在没有 runBlocking 的情况下使用 join。当一个函数被挂起时,它可以挂起它的执行,并且可以加入其他协程。
现在您可以从协程中调用 fetchObjects,就像在代码的第二部分中所做的那样:
fun fetchingFromAPI(onFinished: () -> Unit) {
CoroutineScope(Dispatchers.IO).launch {
fetchObjects(::save)
onFinished()
}
}
可能这需要一些调试,但因为我没有你的其余代码,我无法做到这一点,所以请让我知道是否有任何错误或是否没有像你预期的那样工作。