Библиотека подкачки с настраиваемым источником данных не обновляет строку при обновлении комнаты

Я внедряю новую библиотеку подкачки с RecyclerView с приложением, созданным на основе Компоненты архитектуры.

Данные для заполнения списка берутся из базы данных Room. Фактически, он извлекается из сети, сохраняется в локальной базе данных и передается в список.

Чтобы предоставить необходимые данные для построения списка, я реализовал свой собственный PageKeyedDataSource. Все работает как положено, за исключением одной маленькой детали. Если после отображения списка происходит какое-либо изменение данных элемента строки списка, он не обновляется автоматически. Итак, если, например, в моем списке отображается список элементов с полем name, и внезапно это поле обновляется в локальной базе данных Room для определенного элемента строки, список не обновляет строка UI автоматически.

Такое поведение происходит только при использовании настраиваемого источника данных, в отличие от случая, когда источник данных получается автоматически из DAO, напрямую возвращая DataSource Factory . Однако мне нужно реализовать собственный источник данных.

Я знаю, что его можно обновить, вызвав метод invalidate () в DataSource, чтобы восстановить обновленный список. Однако, если приложение показывает 2 списка одновременно (например, половина экрана каждый), и этот элемент отображается в обоих списках, потребуется вызвать invalidate () для обоих списков отдельно.

Я подумал о решении, в котором вместо использования экземпляра класса элемента для заполнения каждого ViewHolder, он использует LiveData обернутую его версию, чтобы каждая строка отслеживала изменения в своем собственном элементе и при необходимости обновляла пользовательский интерфейс этой строки. Тем не менее, я вижу в этом подходе некоторые недостатки:

  1. Должен быть LifeCycleOwner (например, фрагмент, содержащий RecyclerView). передается в PagedListAdapter, а затем перенаправляет его в ViewHolder для наблюдения обернутый элемент LiveData.
  2. Новый наблюдатель будет зарегистрирован для каждой новой строки списка, поэтому я вообще не знаю, имеет ли он чрезмерные вычислительные затраты и затраты на память, учитывая, что это будет сделано для каждого списка в приложении, в котором много списков.
  3. Поскольку LifeCycleOwner, наблюдающий за обернутым элементом LiveData, будет, например, фрагментом, содержащим RecyclerView, вместо самого ViewHolder, наблюдатель будет уведомляться каждый раз, когда происходит изменение этого элемента, даже если строка, содержащая этот элемент, даже не видимый в этот момент, потому что список был прокручен, что мне кажется пустой тратой ресурсов, которая может излишне увеличить вычислительные затраты.

Я вообще не знаю, может ли это, даже с учетом этих недостатков, показаться приличным подходом или, может быть, кто-нибудь из вас знает какой-либо другой более чистый и лучший способ справиться с этим.

Заранее спасибо.


person Sarquella    schedule 16.05.2018    source источник
comment
Вы когда-нибудь находили решение своей проблемы?   -  person AndroidEnthusiast    schedule 22.02.2019


Ответы (1)


Прошло довольно много времени с момента последней проверки этого вопроса, но для всех, кто интересуется, вот причина моей проблемы + библиотека Я сделал так, чтобы правильно наблюдать за LiveData из ViewHolder (чтобы избежать использования обходного пути, описанного в вопросе) .

Моя конкретная проблема возникла из-за неправильного использования классов данных Kotlin. При их использовании важно отметить, что (как описано в документации), toString (), equals (), hashCode () и copy () будут учитывать только те свойства, которые объявлены в конструкторе класса, игнорируя те, которые объявлены в теле класса. Простой пример:

data class MyClass1(val prop: Int, val name: String) {}

data class MyClass2(val prop: Int) {
    var name: String = ""
}

fun main() {   
    val a = MyClass1(1, "a")
    val b = MyClass1(1, "b")

    println(a == b) //False :) -> a.name != b.name

    val c = MyClass2(2)
    c.name = "c"
    val d = MyClass2(2)
    d.name = "d"

    println(c == d) //True!! :O -> But c.name != d.name
}

Это особенно важно при реализации PagedListAdapter DiffCallback , как если бы мы находимся в сценарии, подобном MyClass2, независимо от того, сколько раз мы обновляем поле name в нашем Room, как areContentsTheSame () DiffCallback. >, вероятно, всегда будет возвращать true, поэтому список никогда не будет обновляться при этом изменении.


Если причина, описанная выше, не является причиной вашей проблемы или вы просто хотите иметь возможность правильно наблюдать за экземплярами LiveData из ViewHolder, я разработал небольшую библиотеку, которая предоставляет Жизненный цикл для любого ViewHolder, что позволяет ему правильно наблюдать за экземплярами LiveData (вместо того, чтобы использовать обходной путь, описанный в вопросе) .

https://github.com/Sarquella/LifecycleCells

person Sarquella    schedule 30.09.2019