Приведи пример когда вычисление DiffUtil не стоит делать в фоновом потоке

Ответ

DiffUtil работает эффективнее в UI-потоке, если список небольшой (~до 100 элементов). Передача данных между потоками может занять больше времени, чем само вычисление diff.

// Неоптимально для маленьких списков:
viewModelScope.launch(Dispatchers.Default) {
    val diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList))
    withContext(Dispatchers.Main) {
        adapter.submitList(newList, diffResult)
    }
}

// Лучше для малых списков:
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList))
adapter.submitList(newList, diffResult)

Для больших списков (>1000 элементов) DiffUtil стоит выносить в фоновый поток.

Ответ 18+ 🔞

А, ну вот это реально интересный момент, про который многие нихуя не думают! Смотри, как бывает: народ взял моду — всё, что связано с DiffUtil, сразу в бэкграунд пихать. А потом сидят, ебать колотить, и чешут репу: "Чё-то у меня список из 20 элементов тормозит при обновлении, хуй поймёшь".

Вот в чём прикол: сам по себе DiffUtil — алгоритм, конечно, не из лёгких. Но когда элементов-то — хуй с горы, да и того меньше, он отрабатывает быстрее, чем ты успеешь моргнуть. А вот когда ты начинаешь этот расчёт в отдельном потоке запускать, а потом результат обратно в UI-таскаешь... Это ж, ёпта, овердохуища накладных расходов получается! Контекст переключать, корутины гонять, объекты между потоками перекидывать... В итоге получается, что ты ради "оптимизации" сделал только хуже — сам расчёт diff'а занял 2 миллисекунды, а вся эта возня с диспатчерами — все 10.

// Вот это — манда с ушами для маленького списка. Зачем городить?
viewModelScope.launch(Dispatchers.Default) {
    val diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList))
    withContext(Dispatchers.Main) {
        adapter.submitList(newList, diffResult)
    }
}

// А вот это — просто и правильно. Рассчитали на месте и применили.
val diffResult = DiffUtil.calculateDiff(MyDiffCallback(oldList, newList))
adapter.submitList(newList, diffResult)

Но! Тут, как водится, есть вторая сторона медали. Если у тебя список не "хуй с горы", а реально большой — за тысячу элементов, или там сложные объекты с глубоким сравнением — тогда да. Тогда уже терпения ноль ебать, если это в UI-потоке считать. Тут уже надо честно выносить в Dispatchers.Default или в какой другой фоновый, потому что расчёт может и на сотни миллисекунд затянуться, и тогда уже юзер будет видеть реальные фризы. А это, сам понимаешь, пиздец как неприятно.

Так что правило простое, чувак: смотри по размеру списка. Меньше сотни — смело делай в основном потоке, выиграешь в скорости. Больше тысячи — однозначно гони в бэкграунд. А между сотней и тысячей — это уже серая зона, надо смотреть по месту, замерять, и вообще — э, бошка, думай! Иногда и 500 элементов могут быстро посчитаться, если в DiffCallback'е areContentsTheSame лёгкий. А иногда и 200 — заставят анимации дёргаться, если там внутри хитрые сравнения. В общем, бездумно шаблоны не применяй, а то будет тебе хиросима, а не плавные обновления списка.