Search code examples
androidandroid-architecture-componentsandroid-paging

PositionalDataSource refresh on Android


I'm using the PagingLibrary and also implemented the PositionalDataSource, I'm not using Room in this case since my DB was already declared by other developers and I don't want to migrate to Room right now. Instead, it is using GreenDao as a wrapper to Sqlite3.

The issue is that whenever I'm adding/removing/updating an item in DB the view is not updating. even if I'm calling to invalidate the DataSource.

DataSourceFactory:

public class ContactDataSourceFactory extends DataSource.Factory<Integer, Contact> {

    @NonNull
    @Override
    public DataSource<Integer, Contact> create() {
        return new ContactDataSource();
    }
}

ContactDataSource:

class ContactDataSource extends PositionalDataSource<Contact>{

    @Override
    public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Contact> callback) {
         callback.onResult(contactRepository.getContactsInRange(userId, params.requestedStartPosition, params.requestedLoadSize, false, null), 0);
    }

    @Override
    public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Contact> callback) {
         callback.onResult(contactRepository.getContactsInRange(userId, params.startPosition, params.loadSize, false, null));
    }
}

ContactViewModel:

public class ContactViewModel extends ViewModel {

    private final int PAGE_SIZE = 50;

    public final LiveData<PagedList<Contact>> contactList;
    private DataSource contactDataSource;

    public ContactViewModel(){

        PagedList.Config.Builder pageListConfig = new PagedList.Config.Builder();

        pageListConfig
            .setPageSize(PAGE_SIZE)
            .setEnablePlaceholders(false);

        ContactDataSourceFactory contactDataSourceFactory = new ContactDataSourceFactory();
        contactDataSource = contactDataSourceFactory.create();

        contactDataSource.addInvalidatedCallback(new DataSource.InvalidatedCallback() {
            @Override
            public void onInvalidated() {
                // create new DataSource ?
            }
        });
        contactList = new LivePagedListBuilder<>(contactDataSourceFactory, pageListConfig.build()).build();

    }

    public void invalidateDataSource(){
        contactDataSource.invalidate();
    }
}

I'm calling to invalidateDataSource() from my fragment. at first, when I called this method, I saw that the inner implementation of the PagingLibrary is going through a for loop, and check if there is any callback for invalidation. this is the reason I was adding the invalidation callback because if I don't have it, the invalidate does nothing. now I'm not sure how to update the list inside the invalidation callback.


Solution

  • For anyone who encounters this issue, I was able to solve it by these changes in my VIEW_MODEL class:

    public class ContactViewModel extends ViewModel {
    
        private final int PAGE_SIZE = 50;
    
        public final LiveData<PagedList<Contact>> contactList;
        public MutableLiveData<String> filterConstraint = new MutableLiveData<>();
    
        public ContactViewModel(){
    
            PagedList.Config.Builder pageListConfig = new PagedList.Config.Builder();
    
            pageListConfig
                .setPageSize(PAGE_SIZE)
                .setEnablePlaceholders(false);
    
            contactList = Transformations.switchMap(filterConstraint, input -> {
                boolean filtered = !(TextUtils.isEmpty(input) || input.equals("%%"));
                ContactDataSourceFactory contactDataSourceFactory = new ContactDataSourceFactory(filtered, filtered ? input : null);
                return new LivePagedListBuilder<>(contactDataSourceFactory, pageListConfig.build()).build();
            });
        }
    
        public void invalidateDataSource(){
            PagedList<Contact> pagedList = contactList.getValue();
            if (pagedList != null && pagedList.getDataSource() != null)
                pagedList.getDataSource().invalidate();
        }
    }
    

    to refresh, you need to get the pageList from the LiveData, and then invalidate its DataSource.

    This will make an updated DataSource for you, and will automatically update your RecyclerView. so as you can see, there is no need to add the InvalidatedCallBack as I did first when asking the question.