13

我一直在使用RecyclerView实现新的分页库,并在Architecture Components之上构建了一个应用程序。

填充列表的数据来自Room数据库。实际上,它是从网络中获取的,存储在本地数据库中并提供给列表。

为了提供构建列表所需的数据,我实现了自己的自定义PageKeyedDataSource。除了一个小细节外,一切都按预期工作。列表显示后,如果列表行元素的数据发生任何变化,则不会自动更新。因此,例如,如果我的列表显示具有字段name的项目列表,并且突然,该字段在本地 Room 数据库中针对某个行项目进行了更新,则该列表不会自动更新行 UI。

此行为仅在使用自定义 DataSource 时发生,这与从DAO自动获取 DataSource 时不同,通过直接返回DataSource Factory。但是,我需要实现一个自定义数据源。

我知道它可以通过调用 DataSource 上的invalidate()方法来重建更新的列表。但是,如果应用程序一次显示 2 个列表(例如每个半屏),并且该项目出现在两个列表中,则需要分别为两个列表调用invalidate()

我想到了一个解决方案,它不是使用项目类的实例来填充每个ViewHolder,而是使用它的LiveData包装版本,以使每一行观察其自己的项目的变化并在必要时更新该行 UI . 尽管如此,我还是看到了这种方法的一些缺点:

  1. 必须将LifeCycleOwner(例如包含 RecyclerView 的 Fragment)传递给PagedListAdapter,然后将其转发给 ViewHolder,以便观察 LiveData 包装的项目。
  2. 将为每个列表的新行注册一个新的观察者,所以我根本不知道它是否有过多的计算和内存成本,考虑到它将为应用程序中的每个列表完成,其中有很多列表。
  3. 由于 LifeCycleOwner 观察 LiveData 包装的项目将是,例如,包含 RecyclerView 的 Fragment,而不是 ViewHolder 本身,因此每次该项目发生更改时都会通知观察者,即使包含该项目的行不是偶数在那一刻可见,因为列表已滚动,在我看来,这似乎是一种资源浪费,可能会不必要地增加计算成本。

我根本不知道,即使考虑到这些缺点,它是否看起来是一种不错的方法,或者,也许你们中的任何人都知道任何其他更清洁、更好的管理方法。

先感谢您。

4

1 回答 1

1

Quite some time since last checked this question, but for anyone interested, here is the cause of my issue + a library I made to observe LiveData properly from a ViewHolder (to avoid having to use the workaround explained in the question).

My specific issue was due to a bad use of Kotlin's Data Classes. When using them, it is important to note that (as explained in the docs), the toString(), equals(), hashCode() and copy() will only take into account all those properties declared in the class' constructor, ignoring those declared in the class' body. A simple example:

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
}

This is specially important when implementing the PagedListAdapter's DiffCallback, as if we are in a example's MyClass2 like scenario, no matter how many times we update the name field in our Room database, as the DiffCallback's areContentsTheSame() method is probably always going to return true, making the list never update on that change.


If the reason explained above is not the reason of your issue, or you just want to be able to observe LiveData instances properly from a ViewHolder, I developed a small library which provides a Lifecycle to any ViewHolder, making it able to observe LiveData instances the proper way (instead of having to use the workaround explained in the question).

https://github.com/Sarquella/LifecycleCells

于 2019-09-30T18:39:02.430 回答