Пейджинговая библиотека с настраиваемым DataSource, не обновляющим строку на Room Room
Я внедряю новую пейджинговую библиотеку с RecyclerView с приложением, созданным поверх компонентов архитектуры.
Данные для заполнения списка получены из базы данных Room. Фактически, он извлекается из сети, сохраняется в локальной базе данных и предоставляется в список.
Чтобы предоставить необходимые данные для создания списка, я внедрил собственный собственный файл PageKeyedDataSource. Все работает так, как ожидалось, за исключением одной маленькой детали. Когда список отображается, если какие-либо изменения происходят с данными элемента строки списка, он не обновляется автоматически. Итак, если, например, в моем списке отображается список элементов, у которых есть имя поля, и вдруг это поле обновляется в локальной базе данных Room для определенного элемента строки, список не обновляет пользовательский интерфейс строки автоматически.
Такое поведение происходит только при использовании пользовательского источника данных, в отличие от того, когда DataSource автоматически получается из DAO, возвращая напрямую FactorySource Factory. Однако мне нужно реализовать пользовательский DataSource.
Я знаю, что его можно было бы обновить, вызвав метод invalidate() на DataSource, чтобы перестроить обновленный список. Однако, если приложение отображает по 2 списка одновременно (например, половина экрана), и этот элемент отображается в обоих списках, для вызова обоих списков потребуется вызвать invalidate().
Я подумал с решением, в котором вместо того, чтобы использовать экземпляр класса item для заполнения каждого ViewHolder, он использует завершенную версию LiveData, чтобы каждая строка наблюдала за изменениями в своем собственном элементе и обновляла этот интерфейс строки при необходимости, Тем не менее, я вижу некоторые недостатки в этом подходе:
- Например, LifeCycleOwner (например, фрагмент, содержащий RecyclerView) должен быть передан в PagedListAdapter, а затем перенаправлен в ViewHolder, чтобы наблюдать за завершенным элементом LiveData.
- Новый наблюдатель будет зарегистрирован для каждого списка новых строк, поэтому я не знаю вообще, если он имеет чрезмерную вычислительную и стоимость памяти, учитывая, что это будет сделано для каждого списка в приложении, в котором есть много списков.
- Поскольку LifeCycleOwner, наблюдающий за обернутым элементом LiveData, будет, например, фрагментом, содержащим RecyclerView, вместо самого ViewHolder, наблюдатель будет уведомляться каждый раз, когда происходит изменение этого элемента, даже если строка, содержащая этот элемент, видимый в тот момент, потому что список прокручивается, что кажется мне пустой тратой ресурсов, которая может излишне увеличивать вычислительные затраты.
Я вообще не знаю, если даже учитывая эти недостатки, это может показаться приличным подходом или, может быть, если кто-то из вас знает какой-нибудь другой чист и лучший способ справиться с этим.
Заранее спасибо.
Ответы
Ответ 1
Прошло довольно много времени с тех пор, как последний раз проверял этот вопрос, но для всех, кто интересуется, вот причина моей проблемы + библиотека library, которую я сделал, чтобы правильно наблюдать 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-подобного сценария, независимо от того, сколько раз мы обновляем поле имени в нашей комнате базы данных, поскольку метод DiffCallback areContentsTheSame(), вероятно, всегда будет возвращать значение true, поэтому список никогда не будет обновляться при этом изменении.
Если причина, описанная выше, не является причиной вашей проблемы, или вы просто хотите правильно наблюдать за экземплярами LiveData из ViewHolder, я разработал небольшую библиотеку, которая предоставляет жизненный цикл любому ViewHolder, что позволяет ему наблюдать за экземплярами LiveData. надлежащим образом (вместо того, чтобы использовать обходной путь, описанный в вопросе).
https://github.com/Sarquella/LifecycleCells