PageKeyedDataSource loadAfter doesnt get fire

pagekeyeddatasource invalidate
android paging library swipe refresh
pagedlistadapter
android paging library error handling
android paging library tutorial using volley
android paging library github
paging library android kotlin
android pagedlist add item

In my fragment, I integrated android jetpack paging library and the data source I using PageKeyedDataSource with retrofit API callback.

the code runs as excepted and loads the data to recyclerview but after I scroll to the bottom, it supposed to load more data by firing loadAfter function in data source class but it didn't

I also switched to ItemKeyedDataSource still it fails to execute

is my code wrong or the plugin have an issue! but in some demo apps which I found in GitHub works perfectly I followed there code. please if anyone had this problem and fixed let me know Edit: Using AndroidX

public class ReportsDataSource extends PageKeyedDataSource<Integer, ReportItemModel> {

    MutableLiveData<NetworkState> networkState = new MutableLiveData<>();
    MutableLiveData<NetworkState> initialLoad = new MutableLiveData<>();

    private UserService getUserService;
    private List<Call<?>> compositeDisposable;
    private CompositeDisposable compositeDisposableData;
    private SchedulerProvider schedulerProvider;
    private Completable retryCompletable = null;

    public ReportsDataSource(UserService getUserService, List<Call<?>> compositeDisposable, CompositeDisposable compositeDisposableData, SchedulerProvider schedulerProvider) {
        this.getUserService = getUserService;
        this.compositeDisposable = compositeDisposable;
        this.compositeDisposableData = compositeDisposableData;
        this.schedulerProvider = schedulerProvider;
    }


    @Override
    public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, ReportItemModel> callback) {

        networkState.postValue(NetworkState.LOADING);
        initialLoad.postValue(NetworkState.LOADING);

        loadPage(1, new callback() {
            @Override
            public void get(ReportListModel list) {
                setRetry(null);
                networkState.postValue(NetworkState.LOADED);
                initialLoad.postValue(NetworkState.LOADED);
                callback.onResult(list.data.items, 1, list.data.next);
            }

            @Override
            public void failure(Throwable t) {
                setRetry(() -> loadInitial(params, callback));
                NetworkState error = NetworkState.error(t.getMessage());
                networkState.postValue(error);
                initialLoad.postValue(error);
            }
        });
    }

    @Override
    public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, ReportItemModel> callback) {

    }

    @Override
    public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, ReportItemModel> callback) {

        networkState.postValue(NetworkState.LOADING);

        loadPage(params.key, new callback() {
            @Override
            public void get(ReportListModel list) {
                setRetry(null);
                networkState.postValue(NetworkState.LOADED);
                callback.onResult(list.data.items, list.data.next != params.key ? null : list.data.next);
            }

            @Override
            public void failure(Throwable t) {
                setRetry(() -> loadAfter(params, callback));
                networkState.postValue(NetworkState.error(t.getMessage()));
            }
        });
    }


    public static void log(String msg) {
        boolean threadMain = Looper.getMainLooper().getThread() == Thread.currentThread();
        Timber.tag("Thread_finder_" + (threadMain ? "ui" : "none")).d(Thread.currentThread().getId() + " " + msg);
    }

    private void loadPage(int i, callback callback) {
        log("loadPage");
        Call<ReportListModel> call = getUserService.getReportsList(i);
        compositeDisposable.add(call);
        try {
            Response<ReportListModel> response = call.execute();
            log("onResponse");
            if (RetrofitHelper.isSuccessful(response)) {
                callback.get(response.body());
            } else {
                callback.failure(new Throwable("Model verification is failed"));
            }
        } catch (IOException e) {
            callback.failure(e);
            //e.printStackTrace();
        }
    }

    public void retry() {
        if (retryCompletable != null) {
            compositeDisposableData.add(retryCompletable.subscribeOn(schedulerProvider.io()).observeOn(schedulerProvider.ui()).subscribe(() -> {
            }, Timber::d));
        }
    }

    private void setRetry(Action action) {
        if (action == null) {
            this.retryCompletable = null;
        } else {
            this.retryCompletable = Completable.fromAction(action);
        }
    }

    @NonNull
    public MutableLiveData<NetworkState> getNetworkState() {
        return networkState;
    }

    @NonNull
    public MutableLiveData<NetworkState> getInitialLoad() {
        return initialLoad;
    }


    public interface callback {
        void get(ReportListModel list);

        void failure(Throwable t);
    }
}

ViewModel

public class ReportsListViewModel extends ViewModel {

    private static final int PAGE_SIZE = 10;
    private Executor executor = Executors.newFixedThreadPool(5);
    public LiveData<PagedList<ReportItemModel>> list;
    private List<Call<?>> compositeDisposable = new ArrayList<>();
    private CompositeDisposable compositeDisposableData = new CompositeDisposable();
    private ReportsDataSourceFactory sourceFactory;


    public final ObservableBoolean isErrorMessageVisible;
    public final ObservableBoolean isRetryButtonVisible;
    public final ObservableBoolean isLoadingProgressBarVisible;
    public final ObservableBoolean isSwipeRefreshLayoutEnable;
    public final ObservableField<String> errorMessage;


    public ReportsListViewModel(UserService userService, SchedulerProvider schedulerProvider) {
        sourceFactory = new ReportsDataSourceFactory(userService, compositeDisposable, compositeDisposableData, schedulerProvider);
        PagedList.Config config = new PagedList.Config.Builder()
                .setPageSize(PAGE_SIZE)
                .setEnablePlaceholders(false)
                .build();

        list = new LivePagedListBuilder<>(sourceFactory, config).build();

        isErrorMessageVisible = new ObservableBoolean(false);
        errorMessage = new ObservableField<>("");
        isRetryButtonVisible = new ObservableBoolean(false);
        isLoadingProgressBarVisible = new ObservableBoolean(true);
        isSwipeRefreshLayoutEnable = new ObservableBoolean(true);
    }


    @Override
    protected void onCleared() {
        super.onCleared();
        RetrofitStatic.clearRetrofitList(compositeDisposable);
        compositeDisposableData.clear();
    }

    public void retry() {
        sourceFactory.getDataSourceLiveData().getValue().retry();
    }

    public void refresh() {
        sourceFactory.getDataSourceLiveData().getValue().invalidate();
    }

    public LiveData<NetworkState> getNetworkState() {
        return Transformations.switchMap(sourceFactory.getDataSourceLiveData(), ReportsDataSource::getNetworkState);
    }

    public LiveData<NetworkState> getRefreshState() {
        return Transformations.switchMap(sourceFactory.getDataSourceLiveData(), ReportsDataSource::getInitialLoad);
    }

    public void setInitialLoadingState(NetworkState networkState) {
        isErrorMessageVisible.set((networkState.getMessage() != null));
        errorMessage.set(networkState.getMessage());

        isRetryButtonVisible.set(networkState.getStatus() == NetworkStateStatus.FAILED);
        isLoadingProgressBarVisible.set(networkState.getStatus() == NetworkStateStatus.RUNNING);
        isSwipeRefreshLayoutEnable.set(networkState.getStatus() == NetworkStateStatus.SUCCESS);
    }
}

PageListAdapter

public class ReportListAdapter extends PagedListAdapter<ReportItemModel, RecyclerView.ViewHolder> {

    public static final DiffUtil.ItemCallback<ReportItemModel> DIFF_CALLBACK = new DiffUtil.ItemCallback<ReportItemModel>() {

        @Override
        public boolean areItemsTheSame(@NonNull ReportItemModel oldItem, @NonNull ReportItemModel newItem) {
            return oldItem.reportId == newItem.reportId;
        }

        @Override
        public boolean areContentsTheSame(@NonNull ReportItemModel oldItem, @NonNull ReportItemModel newItem) {
            return oldItem.equals(newItem);
        }
    };
    private NetworkState networkState = null;
    private RetryCallback retryCallback;

    public ReportListAdapter(RetryCallback retryCallback) {
        super(DIFF_CALLBACK);
        this.retryCallback = retryCallback;
    }

    @Override
    public int getItemViewType(int position) {
        if (hasExtraRow() && position == getItemCount() - 1) {
            return R.layout.item_network_state;
        } else {
            return R.layout.recycler_report_item;
        }
    }

    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view;
        switch (viewType) {
            case R.layout.recycler_report_item:
            default:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recycler_report_item, parent, false);
                return new ViewHolder(view);
            case R.layout.item_network_state:
                view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_network_state, parent, false);
                return new NetWorkStateHolder(view);
        }
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof ViewHolder) {
            bindView((ViewHolder) holder, position, holder.itemView.getContext());
        } else if (holder instanceof NetWorkStateHolder) {
            bindNetworkView((NetWorkStateHolder) holder, position, holder.itemView.getContext());
        }
    }

    private void bindNetworkView(NetWorkStateHolder holder, int position, Context context) {
        NetworkStateItemViewModel mNetworkStateItemViewModel = new NetworkStateItemViewModel(networkState, retryCallback);
        if (holder.binding != null) {
            holder.binding.setViewModel(mNetworkStateItemViewModel);
            holder.binding.executePendingBindings();
        }
    }

    @Override
    public int getItemCount() {
        return super.getItemCount() + (hasExtraRow() ? 1 : 0);
    }

    private void bindView(ViewHolder holder, int position, Context context) {
        holder.binding.reportId.setText( "Report ID: "+position);
    }

    private boolean hasExtraRow() {
        return networkState != null && networkState != NetworkState.LOADED;
    }

    public void setNetworkState(NetworkState newNetworkState) {
        if (getCurrentList() != null) {
            if (getCurrentList().size() != 0) {
                NetworkState previousState = this.networkState;
                boolean hadExtraRow = hasExtraRow();
                this.networkState = newNetworkState;
                boolean hasExtraRow = hasExtraRow();
                if (hadExtraRow != hasExtraRow) {
                    if (hadExtraRow) {
                        notifyItemRemoved(super.getItemCount());
                    } else {
                        notifyItemInserted(super.getItemCount());
                    }
                } else if (hasExtraRow && previousState != newNetworkState) {
                    notifyItemChanged(getItemCount() - 1);
                }
            }
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        private final RecyclerReportItemBinding binding;

        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            binding = DataBindingUtil.bind(itemView);
        }
    }

    public class NetWorkStateHolder extends RecyclerView.ViewHolder {
        private final ItemNetworkStateBinding binding;
        private final NetworkStateItemViewModel mNetworkStateItemViewModel;

        public NetWorkStateHolder(@NonNull View itemView) {
            super(itemView);
            mNetworkStateItemViewModel = new NetworkStateItemViewModel(networkState, retryCallback);
            binding = DataBindingUtil.bind(itemView);
            binding.setViewModel(mNetworkStateItemViewModel);
            binding.executePendingBindings();
        }
    }
}

Fragment:

public class ReportsFragment extends ParentFragment implements RetryCallback {

    private ReportsFragment.callback callback;
    private FragmentReportsListBinding binding;
    private ReportsListViewModel reportViewModel;
    private ReportListAdapter adapter;


    public void setup(callback callback) {
        this.callback = callback;
    }

    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        super.onCreateView(inflater, container, savedInstanceState);
        binding = DataBindingUtil.inflate(inflater, R.layout.fragment_reports_list, container, false);
        reportViewModel = ViewModelProviders.of(this, mViewModelFactory).get(ReportsListViewModel.class);
        binding.setViewModel(reportViewModel);
        binding.executePendingBindings();
        initAdapter();
        initSwipeToRefresh();
        return binding.getRoot();
    }

    private void initSwipeToRefresh() {
        reportViewModel.getRefreshState().observe(this, networkState -> {
            if (adapter.getCurrentList() != null) {
                if (adapter.getCurrentList().size() > 0) {
                    binding.usersSwipeRefreshLayout.setRefreshing(networkState != null && networkState.getStatus() == NetworkState.LOADING.getStatus());
                } else {
                    setInitialLoadingState(networkState);
                }
            } else {
                setInitialLoadingState(networkState);
            }
        });
    }

    private void setInitialLoadingState(NetworkState networkState) {
        reportViewModel.setInitialLoadingState(networkState);
    }

    private void initAdapter() {
        LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());
        adapter = new ReportListAdapter(this);
        binding.recycler.setLayoutManager(linearLayoutManager);
        binding.recycler.setAdapter(adapter);
        reportViewModel.list.observe(this,  adapter::submitList);
        reportViewModel.getNetworkState().observe(this, adapter::setNetworkState);
    }

    @Override
    public void retry() {
        reportViewModel.retry();
    }

    public interface callback {

    }
}

XML

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <import type="android.view.View" />

        <variable
            name="viewModel"
            type="dasarahalli.portal.adapters.paging.reports.ReportsListViewModel" />
    </data>


    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <androidx.swiperefreshlayout.widget.SwipeRefreshLayout
            android:id="@+id/usersSwipeRefreshLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:enabled="@{viewModel.isSwipeRefreshLayoutEnable}"
            app:onRefreshListener="@{viewModel::refresh}">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />
        </androidx.swiperefreshlayout.widget.SwipeRefreshLayout>

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:orientation="vertical"
            android:padding="8dp">

            <TextView
                android:id="@+id/errorMessageTextView"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:text="@{viewModel.errorMessage}"
                android:visibility="@{viewModel.isErrorMessageVisible ? View.VISIBLE : View.GONE}" />

            <ProgressBar
                android:id="@+id/loadingProgressBar"
                style="?android:attr/progressBarStyle"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:visibility="@{viewModel.isLoadingProgressBarVisible ? View.VISIBLE : View.GONE}" />

            <Button
                android:id="@+id/retryLoadingButton"
                style="@style/Widget.AppCompat.Button.Colored"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center"
                android:onClick="@{() -> viewModel.retry()}"
                android:text="RETRY"
                android:visibility="@{viewModel.isRetryButtonVisible ? View.VISIBLE : View.GONE}" />

        </LinearLayout>

    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>

In the above code as you can see I didn't have called getitem under onBindViewHolder but once after I requested for getItem(itemPostion) it all started working has it supposed to be

Demo Project:

androidx-paging-library-demo-java

PagingWithNetwork: loadAfter() not working correctly after invalidate , Change the pageSize to 1 or 2 in SubRedditViewModel . Then, after a swipe-to-​refresh (so invalidate() is called) the loadAfter() method is not  Questions: I’m using the new Android Paging library to get a RecyclerView with infinite scrolling. I cannot understand why the library does not fire the loadAfter() method when I setup my PagedList like this: val config: PagedList.Config = PagedList.Config.Builder() .setEnablePlaceholders(false) .setPageSize(10) .setPrefetchDistance(1) .setInitialLoadSizeHint(10) .build() val pageList

Apart from not calling getItem in onBindViewHolder, Not setting page size in config can also cause this problem, make sure you called: setPageSize(your_page_size) like this:

PagedList.Config.Builder()
            .setEnablePlaceholders(false)
            .setPageSize(20)
            .build() 

PageKeyedDataSource, Sometimes, the lists that you want to display have a fixed size; there's often just too much information to display all Uses less data as it doesn't need the full dataset. Fire up Android Studio 3.1.3 or later and import the starter project. PageKeyedDataSource if pages you load embed next/previous keys. 3 PageKeyedDataSource loadAfter doesnt get fire Jul 16 '18. 2 Android Device Monitor File Explorer not working with emulator running API 24+ Oct 14 '17.

I have figured it out. The main problem is the Adapter extends PagedListAdapter:

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
    holder.textView.text = "position $position"
}

I use above code to make every item different, and method loadAfter is not being invoked. I read the source code in AsyncPagedListDiffer

   @SuppressWarnings("WeakerAccess")
    @Nullable
    public T getItem(int index) {
        if (mPagedList == null) {
            if (mSnapshot == null) {
                throw new IndexOutOfBoundsException(
                        "Item count is zero, getItem() call is invalid");
            } else {
                return mSnapshot.get(index);
            }
        }

        mPagedList.loadAround(index);
        return mPagedList.get(index);
    }

loadAfter is being invoked here. so we must use getItem in onBindViewHolder

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        holder.textView.text = getItem(position)?.name
    }

Just work!

Paging Library for Android With Kotlin: Creating Infinite Lists , Paging Library: Saving data in the DB doesn't trigger the UI changes - android. The thing is that I have everything set and It's actually kinda working. any): class UserProfileDataSource(val userDao: UserDao): PageKeyedDataSource<​Int, 0), 0, PAGE_SIZE) } override fun loadAfter(params: LoadParams<Int>, callback:  Paging library has a different type of DataSources for the different use case. You can have a PositionalDataSource, an ItemKeyedDataSource, and a PageKaydDataSource.In this tutorial, we discussed different use case of DataSource and how they load initial data, next page and reload data when data invalidate.

Paging Library: Saving data in the DB doesn't trigger the UI changes , An ActivityResultContract that doesn't do any type conversion, taking raw Intent as A callback class for custom tabs client to get messages regarding events in their LoadCallback) and loadAfter(LoadParams, LoadCallback) to return data. Callback for PageKeyedDataSource loadBefore(LoadParams, LoadCallback)​  Most issues with your Kindle Fire, such as a frozen screen or issues with downloading content, can be solved by restarting your device. Go to Slow or Frozen Kindle E-Reader or Fire Tablet to learn more.

Class Index | Desarrolladores de Android, PagedList is immutable and does not support modification. I have read that class MyDataSource<SomeItem> : PageKeyedDataSource<Int, SomeItem>() { override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, SomeItem>) { Have to fire editor after snippets, if snippets enabled my kindle fire HD8 (6th generation comes on with an ad for a game but I cant get it off or get to the menu. It's charged but I can't get to menu or anything else. What's going on/ …

Modifying PagedList in Android Paging Architecture library, In real work use cases, we might have multiple item view types, additional if you have 1 model type with 1 view type at a time (e.g. PageKeyedDataSource<Int​, this approach becomes error-prone and doesn't solve the core problem: multiple (previous page in {@link #loadBefore} * / next page in {@link #​loadAfter}),  The Amazon Fire TV Stick has quite possibly become the most popular device for Kodi users. Fire Sticks are cheap, reasonably powerful, easy to set up and use and make Kodi installation easy. But when it comes to Kodi, you’re not likely to get any help from Amazon support if you encounter issues.

Comments
  • where do you use a custom PagedListAdapter? tried some simple DataSources from dozens of tutorials available on the net?
  • @pskink I have other demo projects which run PagedListAdapter. those are working but the problem is in my application.
  • i still cant see how/where you are using your ReportListAdapter - what is you Activity / Fragment like?
  • @pskink its fragment. updated sample.
  • setup a breakpoint in PagedListAdapter#getItem method and see why PagedList#loadAround does not work as expected
  • This is very odd behavior and I couldn't find any documentation hinting towards it from Google. Sure am glad you pointed it out
  • I had a similar issue, and managed to find the problem because I realised onBindViewHolder wasn't getting called. Thanks!