如何使用 Kotlin 在 Android 上高效地进行带有进度条和返回函数的 API 调用?

问题描述 投票:0回答:1

我正在根据从 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。当一个函数被挂起时,它可以挂起它的执行,并且可以加入其他协程。

android kotlin kotlin-coroutines coroutine
1个回答
0
投票

现在您可以从协程中调用 fetchObjects,就像在代码的第二部分中所做的那样:

    fun fetchingFromAPI(onFinished: () -> Unit) {
        CoroutineScope(Dispatchers.IO).launch {
            fetchObjects(::save)
            onFinished()
        }
    }

可能这需要一些调试,但因为我没有你的其余代码,我无法做到这一点,所以请让我知道是否有任何错误或是否没有像你预期的那样工作。

© www.soinside.com 2019 - 2024. All rights reserved.