Search code examples
androidkotlinandroid-architecture-components

Missing callback to view with pagination?


I'm attempting to get pagination up and workning with Google's new library, but seeing some odd behavior. I'm not sure where I am going wrong.

I'm followig MVP and also using some dagger injection for testability.

In the view:

val adapter = ItemsAdapter()
viewModel.getItems(itemCategoryId, keywords).observe(this, Observer {
  Log.d(TAG, "Items updated $it")
  adapter.setList(it)
})

The data source factory:

class ItemDataSourceFactory(
        private val api: Api,
        private val retryExecutor: Executor
) : DataSource.Factory<Long, Item> {

    private val mutableLiveData = MutableLiveData<ItemDataSource>()

    override fun create(): DataSource<Long, Item> {
        val source = ItemDataSource(api, retryExecutor)
        mutableLiveData.postValue(source)
        return source
    }
}

The data source:

class ItemDataSource(
        private val api: Api,
        private val retryExecutor: Executor
): ItemKeyedDataSource<Long, Item>() {

    companion object {
        private val TAG = ItemKeyDataSource::class.java
    }

    override fun getKey(item: Item): Long = item.id

    override fun loadBefore(params: LoadParams<Long>, callback: LoadCallback<Item>) {
        // ignored, since we only ever append to our initial load
    }

    override fun loadInitial(params: LoadInitialParams<Long>, callback: LoadInitialCallback<Item>) {
        api.loadItems(1, params.requestedLoadSize)
                .subscribe({
                    Logger.d(TAG, "Page 1 loaded. Count ${params.requestedLoadSize}.\nItems: ${it.items}")
                    callback.onResult(it.items as MutableList<Item>, 0, it.item.size)
                }, {})
    }

    override fun loadAfter(params: LoadParams<Long>, callback: LoadCallback<Item>) {
        api.loadItems(params.key, params.requestedLoadSize)
                .subscribe({
                    Logger.d(TAG, "Page ${params.key} loaded. Count ${params.requestedLoadSize}.\nItems: ${it.items}")
                    callback.onResult(it.itemsas MutableList<Item>)
                }, {})
    }
}

And the view model:

  class ItemsViewModel @Inject internal constructor(
        private val repository: ItemsMvp.Repository
): ViewModel(), ItemsMvp.Model {

    override fun items(categoryId: Long, keywords: String?): LiveData<PagedList<Item>> {
        return repository.items(categoryId, keywords)
    }
}

And the repository layer:

class ItemsRepository @Inject internal constructor(
        private val api: Api,
) : ItemsMvp.Repository {

    companion object {
        const val DEFAULT_THREAD_POOL_SIZE = 5
        const val DEFAULT_PAGE_SIZE = 20
    }

    private val networkExecutor = Executors.newFixedThreadPool(DEFAULT_THREAD_POOL_SIZE)
    private val pagedListConfig = PagedList.Config.Builder()
            .setEnablePlaceholders(false)
            .setInitialLoadSizeHint(DEFAULT_PAGE_SIZE)
            .setPageSize(DEFAULT_PAGE_SIZE)
            .build()

    override fun items(categoryId: Long, keywords: String?): LiveData<PagedList<Item>> {
         val sourceFactory = ItemDataSourceFactory(api, networkExecutor)

        // provide custom executor for network requests, otherwise it will default to
        // Arch Components' IO pool which is also used for disk access
        return LivePagedListBuilder(sourceFactory, pagedListConfig)
                .setBackgroundThreadExecutor(networkExecutor)
                .build()
    }
}

The issue is I'm not getting an update to the view after the first page is loaded.

I see this log from the onCreate():

 Items updated []

but then after when the data source returns the items, I see these logs:

Page 1 loaded. Count 20.
Items: [Item(....)]

BUT I never see the view that's subscribing to the view model get an update to set the list on the adapter. If you are curiousI'm using a PagedListAdapter.


Solution

  • I had two mistakes...

    1. The adapter was never set in the view (fail)
    2. I was extending ItemKeyedDataSource instead of PageKeyedDataSource (double fail)

    I made those two adjustments and now everything is behaving as expected.