Search code examples
androidandroid-roomandroid-architecture-componentsandroid-livedataandroid-paging

Android Paging Library when to request more items when doing an endless scrolling


How to know when to request the server for more items using the new android Paging Library of Architecture Components? For example, if I want to implement an endless scrolling where the data is loaded from server how do I know when I have to request more items.


Solution

  • I use next class for implementing this feature:

    package com.mlsdev.enjoymusic.data.repository;
    
    import android.arch.paging.DataSource;
    import android.arch.paging.TiledDataSource;
    import android.arch.persistence.room.InvalidationTracker;
    import android.support.annotation.NonNull;
    import android.support.annotation.WorkerThread;
    import android.util.Log;
    
    import com.mlsdev.enjoymusic.data.local.DeezerDatabase;
    import com.mlsdev.enjoymusic.data.local.Table;
    
    import java.io.IOException;
    import java.util.List;
    import java.util.Set;
    
    import retrofit2.Call;
    import retrofit2.Response;
    
    /**
     * Created by stafievsky on 09.10.17.
     */
    
    public abstract class PagedNetworkBoundResource<ResultType, RequestType> extends TiledDataSource<ResultType> {
    
        private final InvalidationTracker.Observer mObserver;
        private DeezerDatabase db;
    
        public PagedNetworkBoundResource(DeezerDatabase db) {
            this.db = db;
            mObserver = new InvalidationTracker.Observer(Table.States.PLAY_STATE) {
    
                public void onInvalidated(@NonNull Set<String> tables) {
                    invalidate();
                }
            };
            this.db.getInvalidationTracker().addWeakObserver(mObserver);
        }
    
    
        @Override
        public boolean isInvalid() {
            db.getInvalidationTracker().refreshVersionsSync();
            return super.isInvalid();
        }
    
        @Override
        public int countItems() {
            return DataSource.COUNT_UNDEFINED;
        }
    
        @Override
        public List<ResultType> loadRange(int startPosition, int count) {
            if (startPosition == 0 && count == 20) {
                clearDB();
            }
            fetchFromNetwork(startPosition, count);
            return loadFromDb(startPosition, count);
        }
    
        public abstract void clearDB();
    
        @WorkerThread
        private void fetchFromNetwork(int startPosition, int count) {
            if (createCall(startPosition, count) != null)
                try {
                    Response<RequestType> response = createCall(startPosition, count).execute();
                    if (response.isSuccessful() && response.code() == 200) {
                        saveCallResult(response.body());
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
        }
    
        @WorkerThread
        protected abstract void saveCallResult(@NonNull RequestType item);
    
    
        @WorkerThread
        protected abstract List<ResultType> loadFromDb(int startPosition, int count);
    
        @WorkerThread
        protected abstract Call<RequestType> createCall(int startPosition, int count);
    }
    

    And this implementation of this class:

    public LiveData<PagedList<ChartAlbumDao.Album>> getAlbums() {
    
        return new LivePagedListProvider<Integer, ChartAlbumDao.Album>() {
            @Override
            protected DataSource<Integer, ChartAlbumDao.Album> createDataSource() {
                return new PagedNetworkBoundResource<ChartAlbumDao.Album, ModelList<ChartAlbumEntity>>(db) {
    
                    @Override
                    public void clearDB() {
    
                    }
    
                    @Override
                    protected void saveCallResult(@NonNull ModelList<ChartAlbumEntity> item) {
                        if (item != null) {
                            chartAlbumDao.saveAlbums(item.getItems());
                        }
                    }
    
                    @NonNull
                    @Override
                    protected List<ChartAlbumDao.Album> loadFromDb(int startPosition, int count) {
                        return chartAlbumDao.loadAlbums(count, startPosition);
                    }
    
                    @NonNull
                    @Override
                    protected Call<ModelList<ChartAlbumEntity>> createCall(int startPosition, int count) {
                        return deezerService.getChartAlbums(startPosition, count);
                    }
                };
            }
    
        }.create(0, new PagedList.Config.Builder()
                .setEnablePlaceholders(false)
                .setPageSize(20)
                .setInitialLoadSizeHint(20)
                .build());
    }