改造成功后将数据插入房间数据库,协程问题

Posted

技术标签:

【中文标题】改造成功后将数据插入房间数据库,协程问题【英文标题】:Insert Data into room database after retrofit success, coroutine problem 【发布时间】:2020-10-05 01:47:40 【问题描述】:

我正在使用 Retrofit 从 API 服务器获取新闻记录,如果成功,它将使用 insertAll 将数据写入房间数据库,如下面的代码,但此代码会产生如下错误 无法访问主线程上的数据库,因为它可能会长时间锁定 UI

我尝试使用 Coroutine,withContext(Dispatchers.IO) 但我认为它不正确,谢谢您的帮助

suspend fun refreshNews(queryString: String="", page: Int = 1) 
  withContext(Dispatchers.IO) 
    RetrofitClient.instance.getAllNews(buatObjectNewsQuery(queryString), page)
        .enqueue(object : Callback<NewsGetAllResponse> 
            override fun onFailure(call: Call<NewsGetAllResponse>, t: Throwable) 
                Timber.tag(TAG).i("sorry network error")
            

            override fun onResponse(
                call: Call<NewsGetAllResponse>,
                response: Response<NewsGetAllResponse>
            ) 
                val newslist = response.body()?.asDatabaseModel()
                if (newslist != null) 
                    database.databaseNewsDao.insertAll(*newslist)
                    Timber.tag(TAG).i("dalam refresh jumlah data $newslist.size")
                
            


        )

  

【问题讨论】:

我的回答不能解决你的问题吗? @DominicFischer 我确实为您的答案标记了+1,但仍未测试您的答案,感谢您的详细解释 【参考方案1】:

您的代码应该看起来更像这样。用suspendCoroutine 隔离回调代码,这样withContext(Dispatchers.IO) 之类的东西就可以按预期工作。

suspend fun refreshNews(queryString: String="", page: Int = 1) 
    val call = RetrofitClient.instance.getAllNews(buatObjectNewsQuery(queryString), page)
    val response = suspendCoroutine  cont ->
        call.enqueue(object : Callback<NewsGetAllResponse> 
            override fun onFailure(call: Call<NewsGetAllResponse>, t: Throwable) 
                cont.resumeWithException(t)
            

            override fun onResponse(
                call: Call<NewsGetAllResponse>,
                response: Response<NewsGetAllResponse>
            ) 
                const.resume(response)
            
        )
    

    val newslist = response.body()?.asDatabaseModel()
    if (newslist != null) 
        withContext(Dispatchers.IO) 
            database.databaseNewsDao.insertAll(*newslist)
            Timber.tag(TAG).i("dalam refresh jumlah data $newslist.size")
        
    

编辑:

理想情况下,您希望为此创建一个扩展函数,这样阅读起来就不会那么令人生畏。

suspend fun <T> Call<T>.awaitResponse(): Response<T> 
    return suspendCancellableCoroutine  continuation ->
        continuation.invokeOnCancellation 
            cancel()
        
        enqueue(object : Callback<T> 
            override fun onResponse(call: Call<T>, response: Response<T>) 
                continuation.resume(response)
            

            override fun onFailure(call: Call<T>, t: Throwable) 
                continuation.resumeWithException(t)
            
        )
    


suspend fun refreshNews(queryString: String="", page: Int = 1) 
    val call = RetrofitClient.instance.getAllNews(buatObjectNewsQuery(queryString), page)
    val response = call.awaitResponse()

    val newslist = response.body()?.asDatabaseModel()
    if (newslist != null) 
        withContext(Dispatchers.IO) 
            database.databaseNewsDao.insertAll(*newslist)
            Timber.tag(TAG).i("dalam refresh jumlah data $newslist.size")
        
    

【讨论】:

java.lang.IllegalStateException: 无法在后台线程上调用 setValue。我正在收到此异常 代码中没有setValue。您可能正在使用withContext(Dispatchers.IO) 并在其中调用setValue 是的,我需要从数据库中获取列表的大小,因为我正在使用 livedata value.size 我认为你应该问一个新问题,我没有太多信息可以参考。 @DominicFischer 嗨,我需要做几乎相同的事情,但使用 java。你能看看我的问题吗?我已经设置好了所有东西我只是不知道在哪里做插入():如果 onResponse 或其他地方:***.com/questions/68602466/…

以上是关于改造成功后将数据插入房间数据库,协程问题的主要内容,如果未能解决你的问题,请参考以下文章

如何在房间持久性库中插入图像?

通过Java在间隔后将数据插入数据库[关闭]

在房间数据库中插入值

为啥在执行成功的 bigquery 插入后将其存储在时态表中?

如何从 API 显示图像并在单击图像后将用户发送到视频链接? Kotlin,改造

使用 WebView 加载 url 后将数据插入表单