我正在根据从API获得的大量数据制作一款应用程序.在显示实际的应用程序之前,我需要这些信息,所以我将负责获取这些信息的代码放在加载屏幕上.我将所有信息保存在一个本地数据库(Room数据库)中,这样我就不必在每次启动应用程序时都获取它(因为API没有太大变化),所以我可以实际使用这些信息.

1st problem :当我try 我的代码时,它保存了两次(或更多)对象(可能是因为Coroutines).由于我将fetch是否结束基于many saves happened / number of results,这会导致Activity认为fetch已经完成,停止所有调用.这意味着有些物体没有时间保存.

以下是我的代码(简化):

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数据库中,并更新获取进度(由流控制).

所以我在谷歌上搜索了一下,找到了关于JOBS和runBlocking{}的大约join()个信息,我想"我不需要确切的进度数字,因为它是用ProgressBar显示的,而且我正在处理数千个对象,所以如果我能知道我所有的协程程序什么时候都完成了,并基于这个而不是进度来返回调用,我就很好(即使多个调用浪费了一些资源)."

2nd problem :当我try 使用runBlocking{} or 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{}

如果你还需要我的代码里的任何东西,请告诉我.

推荐答案

运行阻塞阻止您调用它的线程,直到它完成执行. 这可能就是冻结错误的原因,因为您在for循环中阻塞了IO线程,并从那里调用了存储回调. 我建议您挂起所有函数,以便能够在没有运行阻塞的情况下使用Join.当一个函数挂起时,它可以挂起它的执行,并有可能加入其他协程程序.

    class FetchFromAPI {
    suspend 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++

        //Create a list of coroutines and join them all, so the fetchObjects 
        //will return after all the pages are saved
        (1..pageCount).map { i ->
            launch {
                //Get page
                val page = API.getPage(i)
                //Save objects from page
                savePage(page, save)
            }
        }.joinAll()
    }

    private suspend fun savePage(
        page: JSONArray,
        save: (Object) -> Unit
    ) {
        //Same logic here
        (0..page.length()).map { i ->
            launch {
                //Get object
                val obj = page.getJSONObject(i)
                //Save object
                saveObject(obj, save)
            }
        }.joinAll()
    }

    //Here if the save(obj) does not generates any new coroutine you do not need to 
    //use suspend other wise you have to use suspend and make also the save fun 
    //suspend and join the coroutine inside it.
    private fun saveObject(
        obj: JSONObject,
        save: (Object) -> Unit
    ) {
        //Convert Json to Object
        ...

        save(obj)
    }
}

现在你可以像在代码的第二部分那样从协程调用fetchObjects:

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

可能这需要一点调试,但因为我没有你的代码,我不能做它,所以请让我知道,如果有任何错误或如果不是工作像你预期的那样.

在协程中切换线程的示例:

        CoroutineScope(Dispatchers.IO).launch {
            //What ever is inside the fetchObjects will run on the IO thread
            fetchObjects(::save)
            withContext(Dispatchers.Main) {
                //Whatever is inside the onFinished will run on the main thread
                onFinished()
            }
        }

Android相关问答推荐

如何自定义所选的NavigationBar项目?

致命异常:java.lang. SecureExcellent::用户10021和当前进程都没有android. permissions.Change_WIFI_STATE

如何在"不同活动"中添加不同按钮?

显示本地房间数据库中未保存的项目的动态列表

Android 14无法删除已配置的文件

无法在Jetpack Compose中显示Admob原生广告

无法在Android Gradle中同步Chaquopy版本

如何阻止Gradle在编译时生成app-metadata.properties

有没有什么方法可以让Beeware在安卓手机上显示图片?

在c中更新MVVMCross中TextView的Alpha#

AndroidX Media3 迁移指南

如何在 kotlin 中接收带有和不带有可空对象的集合并保持引用相同

了解 CoroutineScope(Job() + Dispatchers.Main) 语法

任务:app:kaptGenerateStubsDebugKotlin执行失败. > 'compileDebugJavaWithJavac' 任务(当前目标是 1.8)

相机2问题:设置AE区域、AF区域和AWB区域.

将 CircularProgressIndicator 添加到按钮而不增加其高度

在 react native 中设置 react-native-paper 组件的样式

如何在 Jetpack Compose 中向图像视图添加对角色带?

可组合的可见性不随状态变化而变化

如何在android studio 2021.1中使用谷歌库以外的库