Android Paging 3 PagingDataAdapter.refresh() followed by retry() causes IllegalStateException: Cannot coerce hint when no pages have loaded

Viewed 5104

I'm implementing paging from network + database cache using the new Paging 3 library. My implementation is similar to Android Paging codelab sample. The most significant difference is that I want to show DB data immediately while network data is loading. I've implemented a RemoteMediator and use a PagingSource provided by Room database.

@Dao
interface FooDao {
  ...
  
  @Query("SELECT * FROM foo")
  fun getFooSource(): PagingSource<Int, Foo>
}

By default no data is displayed in the RecycleView if there's no network connection. To display DB data from PagingSource immediately my RemoteMediator uses InitializeAction.SKIP_INITIAL_REFRESH and it solves the problem.

class MyRemoteMediator(...) : RemoteMediator<Int, Foo>() {
  ...

  override suspend fun initialize(): InitializeAction {
    return InitializeAction.SKIP_INITIAL_REFRESH
  }
}

Now DB data is displayed immediately, but network data isn't loaded so I use PagingDataAdapter.refresh() method to trigger the request. I don't sure where is the proper place for the refresh() call, it doesn't work if I call it inside Fragment.onCreate() for example, so lets delay the call. I don't think it's a right way, just for an example:

class MyFragment : Fragment() {
  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    lifecycleScope.launch {
      delay(1000)
      adapter.refresh()
    }
  }
}

Now it works almost as expected, DB data is displayed immediately and RemoteMediator loads data from the network and updates the database. The only problem, if there's no internet access RemoteMediator request fails and returns MediatorResult.Error. If I retry the request later using PagingDataAdapter.retry() method I get the following error when I scroll the list:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.icesmith.tvprogramepg, PID: 6381
    java.lang.IllegalStateException: Cannot coerce hint when no pages have loaded
        at androidx.paging.PagerState.withCoercedHint$paging_common(PagerState.kt:313)
        at androidx.paging.PageFetcherSnapshot.doLoad(PageFetcherSnapshot.kt:369)
        at androidx.paging.PageFetcherSnapshot$startConsumingHints$2$invokeSuspend$$inlined$collect$1.emit(Collect.kt:137)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.emitAllImpl$FlowKt__ChannelsKt(Channels.kt:59)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt$emitAllImpl$1.invokeSuspend(Unknown Source:11)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:56)
        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
        at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:184)
        at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:108)
        at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:306)
        at kotlinx.coroutines.CancellableContinuationImpl.completeResume(CancellableContinuationImpl.kt:393)
        at kotlinx.coroutines.channels.AbstractChannel$ReceiveElement.completeResumeReceive(AbstractChannel.kt:888)
        at kotlinx.coroutines.channels.ConflatedChannel.offerInternal(ConflatedChannel.kt:62)
        at kotlinx.coroutines.channels.ConflatedBroadcastChannel$Subscriber.offerInternal(ConflatedBroadcastChannel.kt:295)
        at kotlinx.coroutines.channels.ConflatedBroadcastChannel.offerInternal(ConflatedBroadcastChannel.kt:257)
        at kotlinx.coroutines.channels.ConflatedBroadcastChannel.offer(ConflatedBroadcastChannel.kt:238)
        at androidx.paging.PageFetcherSnapshot.addHint(PageFetcherSnapshot.kt:179)
        at androidx.paging.PageFetcher$PagerUiReceiver.addHint(PageFetcher.kt:108)
        at androidx.paging.PagingDataDiffer.get(PagingDataDiffer.kt:113)
        at androidx.paging.AsyncPagingDataDiffer.getItem(AsyncPagingDataDiffer.kt:241)
        at androidx.paging.PagingDataAdapter.getItem(PagingDataAdapter.kt:150)
        at com.icesmith.tvprogramepg.fragment.channellist.ChannelAdapter.onBindViewHolder(ChannelAdapter.kt:31)
        at com.icesmith.tvprogramepg.fragment.channellist.ChannelAdapter.onBindViewHolder(ChannelAdapter.kt:13)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7163)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7243)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6110)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6376)
        at androidx.recyclerview.widget.GapWorker.prefetchPositionWithDeadline(GapWorker.java:288)
        at androidx.recyclerview.widget.GapWorker.flushTaskWithDeadline(GapWorker.java:345)
        at androidx.recyclerview.widget.GapWorker.flushTasksWithDeadline(GapWorker.java:361)
        at androidx.recyclerview.widget.GapWorker.prefetch(GapWorker.java:368)
        at androidx.recyclerview.widget.GapWorker.run(GapWorker.java:399)
        at android.os.Handler.handleCallback(Handler.java:883)
        at android.os.Handler.dispatchMessage(Handler.java:100)
        at android.os.Looper.loop(Looper.java:214)
        at android.app.ActivityThread.main(ActivityThread.java:7356)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)

0 Answers
Related