Библиотека подкачки возвращает изначально пустой список
Я использую библиотеку подкачки для разбиения на страницы списка элементов, которые я получаю с моего сервера. Первоначально, когда мой фрагмент загружен, он возвращает пустой список. Но после изменения фрагментов и возврата к этому фрагменту я вижу загруженный список. После отладки я увидел, что данные на самом деле выбираются, но пустой список был передан моему фрагменту.
ItemDataSource:
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, Item> callback) {
apiService.getItems(OFFSET)
.enqueue(new Callback<ItemWrapper>() {
@Override
public void onResponse(@NonNull Call<ItemWrapper> call,@NonNull Response<ItemWrapper> response) {
callback.onResult(response.body().getItems(), null, OFFSET + 25);
}
@Override
public void onFailure(@NonNull Call<ItemWrapper> call,@NonNull Throwable t) {
t.printStackTrace();
}
});
}
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Item> callback) {
}
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, Item> callback) {
apiService.getItems(params.key)
.enqueue(new Callback<ItemWrapper>() {
@Override
public void onResponse(@NonNull Call<ItemWrapper> call,@NonNull Response<ItemWrapper> response) {
Integer key = response.body().getItems().isEmpty() ? null : params.key + 25;
callback.onResult(response.body().getItems(), key);
}
@Override
public void onFailure(@NonNull Call<ItemWrapper> call,@NonNull Throwable t) {
t.printStackTrace();
}
});
}
ItemDataSourceFactory:
@Override
public DataSource create() {
ItemDataSource itemDataSource = new ItemDataSource();
itemLiveDataSource.postValue(itemDataSource);
return itemDataSource;
}
public MutableLiveData<ItemDataSource> getItemLiveDataSource() {
return itemLiveDataSource;
}
ItemViewModel:
private LiveData<ItemDataSource> liveDataSource;
private LiveData<PagedList<Item>> itemPagedList;
private ItemViewModel(Application application) {
ItemDataSourceFactory factory = new ItemDataSourceFactory();
liveDataSource = factory.getItemLiveDataSource();
PagedList.Config config = (new PagedList.Config.Builder())
.setEnablePlaceholders(false)
.setPageSize(ItemDataSource.LIMIT).build();
itemPagedList = (new LivePagedListBuilder(factory, config)).build();
}
public LiveData<PagedList<Item>> getItems() {
return itemPagedList;
}
Фрагмент:
ItemViewModel itemViewModel = ViewModelProviders.of(this).get(ItemViewModel.class);
itemViewModel.getItems.observe(this, items -> {
adapter.submitList(items);
})
6 ответов
Не уверен на 100%, но я думаю, что это потому, что вы выполняете асинхронный запрос. попробуйте изменить его для синхронного запуска loadInitial()
вот так request.execute()
У меня тоже однажды была эта проблема, и я до сих пор не могу понять, почему она не работает для некоторых фрагментов. Решение, которое я нашел, хотя это скорее быстрое схематичное исправление, заключается в двойной загрузке фрагмента.
Ясин Ажди прав. loadinitial()
немедленно вызывает тот же поток, в котором создан PagedList. Поскольку ваш API асинхронный, метод впервые запускается пустым
Если, как и я, кто-то использует асинхронный вызов RxJava/Kotlin в loadInitial
. Я наконец нашел решение после многих мучительных часов.
Я пробовал использовать отложенный обработчик (500 мс) в Observer
метод, но он был непостоянным и работал не во всех сценариях. Сколько я ни старался сделать синхронным, используяsetFetcher
и Rx observeOn
это не будет работать постоянно.
Мое решение было использовать .blockingSubscribe
в моем Observable. Мой сборщик данных использовал библиотеку Socket, у которой был собственный параллелизм вне моей области, поэтому я не мог гарантировать, что смогу сделать процесс полностью синхронным, как того требует разбиение на страницы. (Процесс, который требует лучшей документации IMO). В любом случае, вот мое решение, надеюсь, оно поможет другим с той же проблемой:
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, ResultItem>
) {
mySocketClientRxRequest()
.subscribe ({
callback.onResult(it.resultItems 1, 2)
},{
it.printStackTrace()
})
}
к
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, ResultItem>
) {
mySocketClientRxRequest()
.blockingSubscribe ({
callback.onResult(it.resultItems 1, 2)
},{
it.printStackTrace()
})
}
Как и в вопросе, я загружал данные асинхронно внутри loadInitial, но данные не поступали в адаптер при вызове обратного вызова.
Решено просто обновлением версии библиотеки с
androidx.paging:paging-runtime-ktx:2.1.2
к
androidx.paging:paging-runtime-ktx:3.0.0
.
У меня была такая же проблема в течение нескольких дней, пока, наконец, я не нашел решение. Но просто отметим, что я использовал Paging версии 3, так что это может не отвечать на этот вопрос напрямую, но может помочь любому, кто все еще борется с пустым recyclerView изначально.
В вашем фрагменте, где вы применяли
PagingAdapter
, использовать
loadStateFlow
на адаптере, чтобы наблюдать за изменениями в
loadState
. Вы можете поместить это в
onCreateView
для фрагмента
val pagingAdapter = PagingAdapter()
lifecycleScope.launch {
pagingAdapter.loadStateFlow.collect { loadState ->
if (loadState.prepend.endOfPaginationReached) {
// Apply this if PagingDataAdapter start binding data in view holder
println("APPLYING ADAPTER")
binding.recyclerView.adapter = pagingAdapter
cancel() // Cancel this flow after applying adapter
}
}
}