当前位置: 首页 > 知识库问答 >
问题:

同步、异步扩散和不一致性检测错误

厉熠彤
2023-03-14
suspend fun updateDataset(newDataset: List<Item>) = withContext(Dispatchers.Default) {
        val diff = DiffUtil.calculateDiff(object : DiffUtil.Callback()
        {
            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
                    = dataset[oldItemPosition].conversation.id == newDataset[newItemPosition].conversation.id

            override fun getOldListSize(): Int = dataset.size
            override fun getNewListSize(): Int = newDataset.size

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean
                    = dataset[oldItemPosition] == newDataset[newItemPosition]
        })

        withContext(Dispatchers.Main) {
            dataset = newDataset // <-- dataset is the Adapter's dataset
            diff.dispatchUpdatesTo(this@ConversationsAdapter)
        }
    }
private fun updateConversationsList(conversations: List<ConversationsAdapter.Item>)
{
    viewLifecycleOwner.lifecycleScope.launch {
        (listConversations.adapter as ConversationsAdapter).updateDataset(conversations)
    }
}

UpdateConversationsList()在很短的时间内被调用多次,因为Kotlin的FlowsFlow 一样调用了这个函数

现在,我有时会得到一个java.lang.IndexOutOfBoundsException:Inconsistency Detection。无效视图保持器适配器PositionViewHolder错误。通过阅读这个线程,我了解到这是一个线程问题,我读过很多类似于这个的建议,都说:更新适配器的数据集的线程和向RecyclerView分派更新的线程必须是相同的。

正如你所看到的,我已经尊重这一点,我做了:

withContext(Dispatchers.Main) {
    dataset = newDataset
    diff.dispatchUpdatesTo(this@ConversationsAdapter)
}

既然主线程,而且只有它,做这两个操作,我怎么可能得到这个错误?

共有1个答案

端木狐若
2023-03-14

你的diff是赛车。如果您的更新在短时间内出现两次,则可能会出现这种情况:

Adapter has dataset 1 @Main
Dataset 2 comes
calculateDiff between 1 & 2 @Async
Dataset 3 comes
calculateDiff between 1 & 3 @Async
finished calculating diff between 1 & 2 @ Async
finished calculating diff between 1 & 3 @ Async
Dispatcher main starts handling messages
replace dataset 1 with dataset 2 using 1-2 diff @Main
replace dataset 2 with dataset 3 using 1-3 diff @Main - inconsistency

备选方案是1-3之间的差异,可以在1-2之前完成,但问题不变。当新的计算出现时,您必须取消正在进行的计算,并防止部署无效的diff(例如,在片段中存储作业引用:

var updateJob : Job? = null

private fun updateConversationsList(conversations: List<ConversationsAdapter.Item>)
{
    updateJob?.cancel()
    updateJob = viewLifecycleOwner.lifecycleScope.launch {
        (listConversations.adapter as ConversationsAdapter).updateDataset(conversations)
    }
}

如果取消,则withContext(dispatchers.main)将在内部检查继续状态,并且不会运行。

 类似资料:
  • 问题内容: 同步和异步AJAX调用有什么区别?何时使用同步以及何时异步? 问题答案: 在最基本的级别上,当您希望调用在后台发生时,您可以使用异步模式,而当您希望代码等待直到调用完成时,则可以使用同步模式。 异步模式是AJAX调用的常用方法,因为通常在事件上附加一个回调函数,以便您可以在服务器端数据就绪时进行响应,而不必等待数据到达。

  • 问题内容: 我该如何做这项工作 我试图从异步之一获取同步功能,我需要它来使用FreeTds异步查询作为同步之一 问题答案: 使用deasync-用C ++编写的模块,它将Node.js事件循环暴露给JavaScript。该模块还公开了一个函数,该函数阻止后续代码,但不阻止整个线程,也不引起繁忙的等待。您可以将函数放入循环中:

  • 本文向大家介绍举例说明同步和异步相关面试题,主要包含被问及举例说明同步和异步时的应答技巧和注意事项,需要的朋友参考一下 考察点:线程 如果系统中存在临界资源(资源数量少于竞争资源的线程数量的资源),例如正在写的数据以后可能被另一个线程读到,或者正在读的数据可能已经被另一个线程写过了,那么这些数据就必须进行同步存取(数据库操作中的排他锁就是最好的例子)。当应用程序在对象上调用了一个需要花费很长时间来

  • 我意识到这是一个基本问题,但我没能在别处找到答案。 是

  • 异步操作在线程中执行,与主应用程序线程分开。当应用程序调用方法异步执行操作时,应用程序可以在异步方法执行其任务时继续执行。 示例 下面通过一个例子来理解这个概念。在示例程序中使用IO库接受用户输入。 是一种同步方法。它将阻止执行函数调用之后的所有指令,直到方法完成执行。 等待输入。它停止执行并且在收到用户输入之前不再执行任何操作。 以上示例将产生以下输出 - 在计算中,当某个事件在继续之前等待事件

  • 本文向大家介绍详解XMLHttpRequest(一)同步请求和异步请求,包括了详解XMLHttpRequest(一)同步请求和异步请求的使用技巧和注意事项,需要的朋友参考一下 XMLHttpRequest 让发送一个HTTP请求变得非常容易。你只需要简单的创建一个请求对象实例,打开一个URL,然后发送这个请求。当传输完毕后,结果的HTTP状态以及返回的响应内容也可以从请求对象中获取。  通过XML