【问题标题】: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,但仍未测试您的答案,感谢您的详细解释

标签: kotlin coroutine kotlin-coroutines


【解决方案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 或其他地方:stackoverflow.com/questions/68602466/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多