So, I've playing with Android's new Paging Library part of the JetPack tools.
I'm doing a very basic demo App where I show a list of random user profiles that I get from the RandomUser API.
The thing is that I have everything set and It's actually kinda working.
In my the list's fragment I'm listening for the DB changes:
...
mainViewModel.userProfiles.observe(this, Observer { flowableList ->
usersAdapter.submitList(flowableList) })
...
(I tried with both, using Flowables
and Observables
with the RxPagedListBuilder
and now I'm using LiveData
with the LivePagedListBuilder
. I'd like to use the RxPagedListBuilder
if it's possible)
In my ViewModel I'm setting the LivePagedListBuilder
...
private val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPrefetchDistance(UserProfileDataSource.PAGE_SIZE / 2) //Is very important that the page size is the same everywhere!
.setPageSize(UserProfileDataSource.PAGE_SIZE)
.build()
val userProfiles: LiveData<PagedList<UserProfile>> = LivePagedListBuilder(
UserProfileDataSourceFactory(userProfileDao), config)
.setBoundaryCallback(UserProfileBoundaryCallback(randomUserRepository, userProfileDao))
.build()
...
I've set the DataSource to fetch the data form my DB (if there's any):
class UserProfileDataSource(val userDao: UserDao): PageKeyedDataSource<Int, UserProfile>() {
companion object {
const val PAGE_SIZE = 10
}
override fun loadInitial(params: LoadInitialParams<Int>, callback: LoadInitialCallback<Int, UserProfile>) {
callback.onResult(userDao.getUserProfileWithLimitAndOffset(PAGE_SIZE, 0), 0, PAGE_SIZE)
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, UserProfile>) {
callback.onResult(userDao.getUserProfileWithLimitAndOffset(PAGE_SIZE, params.key), params.key + PAGE_SIZE)
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, UserProfile>) {
//This one is not used
}
}
And I've set the BoundaryCallback to fetch the data from network if the database is empty:
class UserProfileBoundaryCallback(val userRepository: RandomUserRepository,
val userDao: UserDao) : PagedList.BoundaryCallback<UserProfile>() {
/**
* Database returned 0 items. We should query the backend for more items.
*/
override fun onZeroItemsLoaded() {
userRepository.getUserProfiles(0, UserProfileDataSource.PAGE_SIZE)
.subscribeOn(Schedulers.io()) //We shouldn't write in the db over the UI thread!
.subscribe(object: DataSourceSubscriber<List<UserProfile>>() {
override fun onResultNext(userProfiles: List<UserProfile>) {
userDao.storeUserProfiles(userProfiles)
}
})
}
/**
* User reached to the end of the list.
*/
override fun onItemAtEndLoaded(itemAtEnd: UserProfile) {
userRepository.getUserProfiles(itemAtEnd.indexPageNumber + 1, UserProfileDataSource.PAGE_SIZE)
.subscribeOn(Schedulers.io()) //We shouldn't write in the db over the UI thread!
.subscribe(object: DataSourceSubscriber<List<UserProfile>>() {
override fun onResultNext(userProfiles: List<UserProfile>) {
userDao.storeUserProfiles(userProfiles)
}
})
}
}
The thing is that the Flowable
, Observable
or LiveData
in the UI only gets triggered if the data is coming from the database, when the UserProfileDataSource
returns some result from database. If the UserProfileDataSource
doesn't return any result, the BoundaryCallback gets called, the request to the API is successfully done, the data gets stored in the database, but the Flowable
never gets triggered. If I close the App and open it again, is gonna fetch the data that we just got from the database and then is going to display it properly.
I've tried every possible solution that I saw in all the articles, tutorials and github projects that I searched.
As I said I tried to change the RxPagedListBuilder
to a LivePagedListBuilder
.
When using RxPagedListBuilder
I tried with both, Flowables
and Observables
.
I tried setting different configurations for the PagedList.Config.Builder
using different Schedulers.
I'm really out of options here, I've been all day long struggling with this, any tip will be appreciated.
If you need more info about the code, just leave me a comment and I'll update the post.
Update:
I've submitted the full source code to my Github repo