10

I use Paging Library to paginate my data set. What I'm trying to do is to refresh the RecyclerView after data in my database has been changed.

I have this LiveData:

val listItems: LiveData<PagedList<Snapshot>> = object : LivePagedListProvider<Long, Snapshot>() {
        override fun createDataSource() = SnapshotsDataSource()
    }.create(null, PagedList.Config.Builder()
        .setPageSize(PAGE_SIZE)
        .setInitialLoadSizeHint(PAGE_SIZE)
        .setEnablePlaceholders(false)
        .build()
    )

And the DataSource:

class SnapshotsDataSource : KeyedDataSource<Long, Snapshot>(), KodeinGlobalAware {

    val db: FirebaseDb = instance()

    override fun getKey(item: Snapshot): Long = item.timestamp

    override fun loadInitial(pageSize: Int): List<Snapshot> {
        val result = db.getSnapshotsTail(pageSize)
        return result
    }

    override fun loadAfter(key: Long, pageSize: Int): List<Snapshot> {
        val result = db.getSnapshotsTail(key, pageSize)
        return result.subList(1, result.size)
    }

    override fun loadBefore(key: Long, pageSize: Int): List<Snapshot> {
        return emptyList()
    }

}

The Adapter is straight forward, so i omit it here.

I've tried to do this when database is modified:

fun reload(position) {
    listItems.value!!.loadAround(position)
}

but it didn't help.

ianhanniballake
  • 191,609
  • 30
  • 470
  • 443
deviant
  • 3,539
  • 4
  • 32
  • 47
  • 1
    call `DataSource#invalidate()` method - it will create a new `DataSource` though... – pskink Oct 28 '17 at 10:44
  • 1
    when i call `invalidate()` nothing happens. it just stops further data loading when scroll RecyclerView – deviant Oct 28 '17 at 11:28
  • if you call `invalidate` then `LivePagedListProvider#createDataSource` should be called – pskink Oct 28 '17 at 11:30
  • yes. in some reason it called billion times (indefinite loop). looks like a bug in the lib. anyway it's not clear how invalidate can be helpful. it will invalidate whole list while i need only 1 or few items. – deviant Oct 28 '17 at 12:42
  • 1
    no, no billion times, `invalidate()` performs its action only once, the docs say: `"Signal the data source to stop loading, and notify its callback. If invalidate has already been called, this method does nothing."` - i have used it and it works just fine - if in your case it was not called at all and now it is called billion times then it seems that your code is somehow broken – pskink Oct 28 '17 at 12:50
  • you are right. i was returning old instance in the `createDataSource`. that is why it's looped. now list is refreshed the list from blank state. now it would be nice to find the way to not drop previous data and just merge old dataset with new dataset – deviant Oct 28 '17 at 13:07
  • Just a little tip, I gave up on using `DataSource`/`DataSourceFactory` in my project, I was getting a lot of weird bugs. Instead of that I added a query method in my DAO interface that returns a `DataSource.Factory` and I use it directly from there. – 4gus71n May 30 '18 at 13:30

3 Answers3

5

try to call listItems.value!!.datasource.invalidate() not directly DataSource#invalidate()

1

I am having the same issue with a custom Firestore based DataSource. The only way to load a portion of the data without invalidating all of the data and having the UI flash / reload seems to be via integrating with Google's Room ORM library. Unfortunately, this will cache my data twice, once with Firestore, and again with Room which is unnecessary.

See the documentation under Consider How Content Updates Work. The only way to have realtime updates is via implementing Room with the PagedList: If you're loading data directly from a Room database updates get pushed to your app's UI automatically.

AdamHurwitz
  • 9,758
  • 10
  • 72
  • 134
1

It's not possible. You can invalidate the whole list only: datasource.invalidate().

Malachiasz
  • 7,126
  • 2
  • 35
  • 49