How to integrate Android Paging Library with NetworkBoundResource
Asked Answered
B

2

6

My app is using Android's Architecture components library and is displaying a list of items fetched from a paginated REST api with an infinite scroll effect.

What I'm trying to do is to use the Paging Library in conjunction with a NetworkBoundResource, so that when the user scrolls down the list, the next items are fetched from the database and displayed if they exist, and the API is simultaneously called to update items in DB.

I could not find any example of these two patterns cohabiting.

Here is the DAO:

@Query("SELECT * FROM items ORDER BY id DESC")
LivePagedListProvider<Integer,MyItem> loadListPaginated();

Here is my NetworkBoundResource implementation:

public class PagedListNetworkBoundResource extends NetworkBoundResource<PagedList<MyItem>, List<MyItem>> {

    @Override
    protected void saveCallResult(@NonNull List<MyItem> items) {
        // Inserting new items into DB
        dao.insertAll(items);
    }

    @Override
    protected boolean shouldFetch(@Nullable PagedList<MyItem> data) {
        return true;
    }

    @NonNull
    @Override
    protected LiveData<PagedList<MyItem>> loadFromDb() {
        return Transformations.switchMap(dao.loadListPaginated().create(INITIAL_LOAD_KEY, PAGE_SIZE),
                new Function<PagedList<MyItem>, LiveData<List<MyItem>>>() {

            @Override
            public LiveData<PagedList<MyItem>> apply(final PagedList<MyItem> input) {
                // Here I must load nested objects, attach them, 
                // and return the fully loaded items
            }
        });
    }

    @NonNull
    @Override
    protected LiveData<ApiResponse<List<MyItem>>> createCall() {
        // I don't get the current paged list offset to perform a call to the API
        return ...;
    }
}
Budding answered 28/11, 2017 at 16:43 Comment(0)
S
8

I also search lot about NetworkBoundResource i came to conclusion that NetworkBoundResource & Paging Lib its not related to each other. They both have there own functionality

As per article give by google about paging library https://developer.android.com/topic/libraries/architecture/paging.html

1.for loading data from local db you need use DataSource My Dao

@Dao
public interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(User... user);
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    void insert(List<User> users);

    @Query("Select * from User ")
    public abstract DataSource.Factory<Integer,User> getList();
}

2.then requesting data from network we need implement BoundaryCallback class with LivePagedListBuilder

public class UserBoundaryCallback extends PagedList.BoundaryCallback<User> {
    public static final String TAG = "ItemKeyedUserDataSource";
    GitHubService gitHubService;
    AppExecutors executors;
    private MutableLiveData networkState;
    private MutableLiveData initialLoading;
    public UserBoundaryCallback(AppExecutors executors) {
        super();
        gitHubService = GitHubApi.createGitHubService();
        this.executors = executors;
        networkState = new MutableLiveData();
        initialLoading = new MutableLiveData();
    }
    public MutableLiveData getNetworkState() {
        return networkState;
    }

    public MutableLiveData getInitialLoading() {
        return initialLoading;
    }

    @Override
    public void onZeroItemsLoaded() {
        //super.onZeroItemsLoaded();
        fetchFromNetwork(null);
    }

    @Override
    public void onItemAtFrontLoaded(@NonNull User itemAtFront) {
        //super.onItemAtFrontLoaded(itemAtFront);
    }

    @Override
    public void onItemAtEndLoaded(@NonNull User itemAtEnd) {
        // super.onItemAtEndLoaded(itemAtEnd);
        fetchFromNetwork(itemAtEnd);
    }
    public void fetchFromNetwork(User user) {
        if(user==null) {
            user = new User();
            user.userId = 1;
        }
        networkState.postValue(NetworkState.LOADING);
        gitHubService.getUser(user.userId,20).enqueue(new Callback<List<User>>() {
            @Override
            public void onResponse(Call<List<User>> call, Response<List<User>> response) {
                executors.diskIO().execute(()->{
                    if(response.body()!=null)
                        userDao.insert(response.body());
                        networkState.postValue(NetworkState.LOADED);
                });
            }

            @Override
            public void onFailure(Call<List<User>> call, Throwable t) {
                String errorMessage;
                errorMessage = t.getMessage();
                if (t == null) {
                    errorMessage = "unknown error";
                }
                Log.d(TAG,errorMessage);
                networkState.postValue(new NetworkState(Status.FAILED, errorMessage));
            }
        });
    }

}

3.My VM Code to load data from DB + Network

public class UserViewModel extends ViewModel {

    public LiveData<PagedList<User>> userList;
    public LiveData<NetworkState> networkState;
    AppExecutors executor;
    UserBoundaryCallback userBoundaryCallback;

    public UserViewModel() {
        executor = new AppExecutors();
    }
    public void init(UserDao userDao)
    {
        PagedList.Config pagedListConfig =
                (new PagedList.Config.Builder()).setEnablePlaceholders(true)
                        .setPrefetchDistance(10)
                        .setPageSize(20).build();
        userBoundaryCallback = new UserBoundaryCallback(executor);
        networkState = userBoundaryCallback.getNetworkState();
        userList = (new LivePagedListBuilder(userDao.getList(), pagedListConfig).setBoundaryCallback(userBoundaryCallback))
                .build();
    }
}
Sabol answered 24/1, 2018 at 7:0 Comment(0)
D
0

This assumes that each item in the callback has contains an index/offset. Typically that is not the case - the items may only contain ids.

Dallis answered 25/1, 2018 at 23:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.