Updating Paging 3 alpha to stable cause indexing issue Android
Asked Answered
A

2

6

Hey I am using Paging 3 library with ViewPager 2. In which it loads unlimited data.

implementation "androidx.paging:paging-runtime-ktx:3.0.0-alpha07"

DataSource.kt

package com.example.viewpagerexample

import java.util.*

class DataSource(
    private val size: Int = 5,
    private val currentDate: Date,
    private val limitDate: Date?
) {

    fun returnData(pageNumber: Int): List<Date> {

        val dateList = mutableListOf<Date>()
        val startDateForPage = startDate(pageNumber)
        val tempCalendar = Calendar.getInstance()

        tempCalendar.time = startDateForPage
        val lastDateForPage = endDate(startDateForPage)

        while (tempCalendar.time < lastDateForPage) {
            if (limitDate == null ||
                tempCalendar.time.before(limitDate) ||
                tempCalendar.time == limitDate
            ) {
                dateList.add(tempCalendar.time)
                tempCalendar.add(Calendar.DATE, 1)
            } else {
                break
            }
        }
        return dateList
    }

    private fun startDate(pageNumber: Int): Date {
        if (pageNumber == 0) {
            return currentDate
        } else {
            Calendar.getInstance().let {
                it.time = currentDate
                it.add(Calendar.DATE, pageNumber * size)
                return it.time
            }
        }
    }

    private fun endDate(firstDateForPage: Date): Date {
        Calendar.getInstance().let {
            it.time = firstDateForPage
            it.add(Calendar.DATE, size)
            return it.time
        }
    }
}

ViewPagerPagingSource.kt

class ViewPagerPagingSource(
    private val dataSource: DataSource
) : PagingSource<Int, Date>() {

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Date> {
        val position = params.key ?: 0

        return try {
            val data = dataSource.returnData(position)
            LoadResult.Page(
                data = data,
                prevKey = if (data.isEmpty()) null else position - 1,
                nextKey = if (data.isEmpty()) null else position + 1,
                itemsBefore = LoadResult.Page.COUNT_UNDEFINED,
                itemsAfter = LoadResult.Page.COUNT_UNDEFINED
            )
        } catch (exception: IOException) {
            LoadResult.Error(exception)
        }
    }

}

All code working fine. when application starts it loads current date page.

enter image description here

Now when I am updating the library to this version

implementation "androidx.paging:paging-runtime-ktx:3.0.1"

It jumping to -1 page I guess and look like this

enter image description here

I don't understand why this is causing the issue and also i edited ViewPagerPagingSource to implement another method

override fun getRefreshKey(state: PagingState<Int, Date>): Int? {
        return state.anchorPosition
}

I don't understand what is causing the issue after updating the library. I am adding my github repository Example. Please someone suggest me what is going wrong in code?

UPDATE

I tried to learn the paging concept and update my code. Also, do changes as per @dlam suggestion.

Again is jumping 1 page if I pass few days before date as current date. Github

2021-11-21 21:20:40.820 5460-5460/com.example.viewpagerexample E/Page -1: [06/11/2021, 07/11/2021, 08/11/2021, 09/11/2021, 10/11/2021]
2021-11-21 21:20:40.821 5460-5460/com.example.viewpagerexample E/Page 0: [11/11/2021, 12/11/2021, 13/11/2021, 14/11/2021, 15/11/2021]
2021-11-21 21:20:40.821 5460-5460/com.example.viewpagerexample E/Page 1: [16/11/2021, 17/11/2021, 18/11/2021, 19/11/2021, 20/11/2021]

UPDATE 2

After @MuhannadFakhouri answer I tried this logic

package com.example.viewpagerexample

import java.util.*

class DataSource(
    private val size: Int = 5,
    private val currentDate: Date,
    private val limitDate: Date? = null
) {

    fun returnData(pageNumber: Int): Result {
        val dateList = mutableListOf<Date>()

        val startDateForPage = startDate(pageNumber)
        val tempCalendar = Calendar.getInstance()

        tempCalendar.time = startDateForPage
        val lastDateForPage = endDate(startDateForPage)

        var index = size
        while (tempCalendar.time <= lastDateForPage && index-- > 0) {
            dateList.add(tempCalendar.time)
            tempCalendar.add(Calendar.DATE, 1)
        }
        val limitCalendar = Calendar.getInstance().apply { limitDate }
        return Result(
            result = dateList,
            pageNumber - 1,
            if (dateList.lastOrNull()?.let { Calendar.getInstance().apply { time = it } }
                    ?.let {
                        it.get(Calendar.YEAR) == limitCalendar.get(Calendar.YEAR) &&
                                it.get(Calendar.DAY_OF_YEAR) == limitCalendar.get(Calendar.DAY_OF_YEAR)
                    } == true)
                null
            else
                pageNumber + 1
        )
    }

    private fun startDate(pageNumber: Int): Date {
        Calendar.getInstance().let {
            it.time = currentDate
            it.add(Calendar.DATE, pageNumber * size)
            return it.time
        }
    }

    private fun endDate(firstDateForPage: Date): Date? {
        Calendar.getInstance().let {
            it.time = firstDateForPage
            it.add(Calendar.DATE, size)

            limitDate?.let { limit ->
                return if (it.time > limit) limit else it.time
            } ?: run {
                return it.time
            }
        }
    }

    data class Result(
        val result: MutableList<Date>,
        val prevKey: Int?,
        val nextKey: Int?
    )
}

Logs

2022-01-08 23:39:12.601 7886-7886/com.example.viewpagerexample E/Page -1: [24/12/2021, 25/12/2021, 26/12/2021, 27/12/2021, 28/12/2021]
2022-01-08 23:39:12.603 7886-7886/com.example.viewpagerexample E/Page 0: [29/12/2021, 30/12/2021, 31/12/2021, 01/01/2022, 02/01/2022]
2022-01-08 23:39:12.604 7886-7886/com.example.viewpagerexample E/Page 1: [03/01/2022, 04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022]

ScreenShot

enter image description here

It need to show 29/12/2021 date but it showing me 03/01/2022

NEW EXPLANATION IN DETAILS

Youtube Link with detail explanination

First It opens screens in which only one button. when I click on this it will open viewpager screen. Whenever I click it opening different date screen. My main point is If I pass as current date it will open for current date as view pager main screen. If I pass any date for example 29/12/2021 so it will open this date. The new attached video is showing how it behaving. It will show the code, logs and emulator.

First time when I clicked it open 04/01/22 which is index -1 you can check in video

2022-01-09 00:01:03.108 9637-9637/com.example.viewpagerexample E/Page -1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]
2022-01-09 00:01:03.109 9637-9637/com.example.viewpagerexample E/Page 0: [09/01/2022]
2022-01-09 00:01:03.109 9637-9637/com.example.viewpagerexample E/Page 1: []

Second time it happens same

2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page -1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]
2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page 0: [09/01/2022]
2022-01-09 00:01:06.488 9637-9637/com.example.viewpagerexample E/Page 1: []

Third time It opens 09/01/22 which it correct.

2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page -1: [04/01/2022, 05/01/2022, 06/01/2022, 07/01/2022, 08/01/2022]
2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page 0: [09/01/2022]
2022-01-09 00:01:09.181 9637-9637/com.example.viewpagerexample E/Page 1: []

My question is this why is this causing this kind of issue sometimes open correct index and sometime it will open wrong index.

Thanks

Logs

2022-01-09 14:15:25.501 21246-21246/com.example.viewpagerexample D/Debugging paging: load: null java.lang.Throwable
        at com.example.viewpagerexample.ViewPagerPagingSource.load(ViewPagerPagingSource.kt:16)
        at androidx.paging.PageFetcherSnapshot.doInitialLoad(PageFetcherSnapshot.kt:283)
        at androidx.paging.PageFetcherSnapshot.access$doInitialLoad(PageFetcherSnapshot.kt:54)
        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invokeSuspend(PageFetcherSnapshot.kt:163)
        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invoke(Unknown Source:8)
        at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invoke(Unknown Source:4)
        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invokeSuspend(CancelableChannelFlow.kt:30)
        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invoke(Unknown Source:8)
        at androidx.paging.CancelableChannelFlowKt$cancelableChannelFlow$1.invoke(Unknown Source:4)
        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1$1.invokeSuspend(SimpleChannelFlow.kt:57)
        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1$1.invoke(Unknown Source:8)
        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1$1.invoke(Unknown Source:4)
        at kotlinx.coroutines.intrinsics.UndispatchedKt.startUndispatchedOrReturn(Undispatched.kt:89)
        at kotlinx.coroutines.CoroutineScopeKt.coroutineScope(CoroutineScope.kt:264)
        at androidx.paging.SimpleChannelFlowKt$simpleChannelFlow$1$1$producer$1.invokeSuspend(SimpleChannelFlow.kt:52)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith(DispatchedContinuation.kt:375)
        at kotlinx.coroutines.internal.DispatchedContinuationKt.resumeCancellableWith$default(DispatchedContinuation.kt:278)
        at kotlinx.coroutines.DispatchedCoroutine.afterResume(Builders.common.kt:256)
        at kotlinx.coroutines.AbstractCoroutine.resumeWith(AbstractCoroutine.kt:102)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:46)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        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:7697)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
2022-01-09 14:15:25.512 21246-21246/com.example.viewpagerexample D/Debugging paging: load: -1 java.lang.Throwable
        at com.example.viewpagerexample.ViewPagerPagingSource.load(ViewPagerPagingSource.kt:16)
        at androidx.paging.PageFetcherSnapshot.doLoad(PageFetcherSnapshot.kt:406)
        at androidx.paging.PageFetcherSnapshot.access$doLoad(PageFetcherSnapshot.kt:54)
        at androidx.paging.PageFetcherSnapshot$collectAsGenerationalViewportHints$$inlined$collect$1.emit(Collect.kt:135)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.emitAllImpl$FlowKt__ChannelsKt(Channels.kt:62)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.access$emitAllImpl$FlowKt__ChannelsKt(Channels.kt:1)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt$emitAllImpl$1.invokeSuspend(Unknown Source:14)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
        at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:244)
        at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:161)
        at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
        at kotlinx.coroutines.flow.SharedFlowImpl.tryEmit(SharedFlow.kt:368)
        at androidx.paging.HintHandler$HintFlow.setValue(HintHandler.kt:133)
        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:85)
        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:79)
        at androidx.paging.HintHandler$State.modify(HintHandler.kt:119)
        at androidx.paging.HintHandler.processHint(HintHandler.kt:79)
        at androidx.paging.PageFetcherSnapshot.accessHint(PageFetcherSnapshot.kt:197)
        at androidx.paging.PageFetcher$PagerUiReceiver.accessHint(PageFetcher.kt:215)
        at androidx.paging.PagingDataDiffer$collectFrom$2$1$1.invokeSuspend(PagingDataDiffer.kt:175)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        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:7697)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:516)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
2022-01-09 14:15:25.519 21246-21246/com.example.viewpagerexample D/Debugging paging: load: -2 java.lang.Throwable
        at com.example.viewpagerexample.ViewPagerPagingSource.load(ViewPagerPagingSource.kt:16)
        at androidx.paging.PageFetcherSnapshot.doLoad(PageFetcherSnapshot.kt:406)
        at androidx.paging.PageFetcherSnapshot.access$doLoad(PageFetcherSnapshot.kt:54)
        at androidx.paging.PageFetcherSnapshot$collectAsGenerationalViewportHints$$inlined$collect$1.emit(Collect.kt:135)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.emitAllImpl$FlowKt__ChannelsKt(Channels.kt:62)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt.access$emitAllImpl$FlowKt__ChannelsKt(Channels.kt:1)
        at kotlinx.coroutines.flow.FlowKt__ChannelsKt$emitAllImpl$1.invokeSuspend(Unknown Source:14)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.EventLoop.processUnconfinedEvent(EventLoop.common.kt:69)
        at kotlinx.coroutines.DispatchedTaskKt.resumeUnconfined(DispatchedTask.kt:244)
        at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:161)
        at kotlinx.coroutines.CancellableContinuationImpl.dispatchResume(CancellableContinuationImpl.kt:397)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl(CancellableContinuationImpl.kt:431)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeImpl$default(CancellableContinuationImpl.kt:420)
        at kotlinx.coroutines.CancellableContinuationImpl.resumeWith(CancellableContinuationImpl.kt:328)
        at kotlinx.coroutines.flow.SharedFlowImpl.tryEmit(SharedFlow.kt:368)
        at androidx.paging.HintHandler$HintFlow.setValue(HintHandler.kt:133)
        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:85)
        at androidx.paging.HintHandler$processHint$1.invoke(HintHandler.kt:79)
        at androidx.paging.HintHandler$State.modify(HintHandler.kt:119)
        at androidx.paging.HintHandler.processHint(HintHandler.kt:79)
        at androidx.paging.PageFetcherSnapshot.accessHint(PageFetcherSnapshot.kt:197)
        at androidx.paging.PageFetcher$PagerUiReceiver.accessHint(PageFetcher.kt:215)
        at androidx.paging.PagingDataDiffer.get(PagingDataDiffer.kt:271)
        at androidx.paging.AsyncPagingDataDiffer.getItem(AsyncPagingDataDiffer.kt:214)
        at androidx.paging.PagingDataAdapter.getItem(PagingDataAdapter.kt:231)
        at com.example.viewpagerexample.ViewPagerAdapter.onBindViewHolder(ViewPagerAdapter.kt:11)
        at com.example.viewpagerexample.ViewPagerAdapter.onBindViewHolder(ViewPagerAdapter.kt:8)
        at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7254)
        at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7337)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6194)
        at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6460)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6300)
        at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6296)
        at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2330)
        at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1631)
        at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1591)
        at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:668)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4309)
        at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:4012)
        at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4578)
        at android.view.View.layout(View.java:22085)
        at android.view.ViewGroup.layout(ViewGroup.java:6290)
        at androidx.viewpager2.widget.ViewPager2.onLayout(ViewPager2.java:527)
        at android.view.View.layout(View.java:22085)
        at android.view.ViewGroup.layout(ViewGroup.java:6290)
        at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1873
Amyamyas answered 28/10, 2021 at 13:42 Comment(1)
I don't know Paging well enough to see if you're doing something wrong, try creating an issue on Google issue tracker. For now you can use the version which is working for you.Macmacabre
G
1

I noticed you filed a bug here: https://issuetracker.google.com/204328119, but I also wanted to update this answer for other future people reading this issue on SO.

The core issue is that you are starting collection on Paging before ViewPager is ready to start binding this items. You should use lifecycleScope.launchWhenCreated instead of lifecycleScope.launch to fix this:

lifecycleScope.launchWhenCreated {
  viewModel.dataList.collectLatest {
    adapter.submitData(it)
  }
}

The other issue I noticed (which would also workaround this), is that you have placeholders enabled, but aren't passing in itemsBefore or itemsAfter in LoadResult.Page. Enabling placeholders and having a static count would also give your view a list size to bind, but since you pass COUNT_UNDEFINED, Paging is not able to pad the list with null placeholders properly since it has no idea how many to add.

Garnierite answered 2/11, 2021 at 16:26 Comment(4)
what can I pass the value in itemBefore or itemAfrer ?Amyamyas
You just pass the Int number of items before and after that page in LoadResult.Page's constructor. For example if you have [null, null, 3, null], you would pass 2 for itemsBefore and 1 for itemsAfter in a Page whose data contains only 3.Garnierite
I tried to learn paging logic and tried to update my logic and still it causing this same issue Github.Amyamyas
In my ViewPagerViewModel I create two functions currentDate in which I am passing a few days before the date from the current date. It again causing the same issue to start from index 1. But if I pass current date it working fine in limitDate. Do you know why this is causing this issue?Amyamyas
A
-1

I didn't look in-deep into this and how this can be related to library's version, but i noticed a glitch in your Datasource logic you're doing the following check

                if (lastDateForPage == limitDate)
                    null
                else
                    pageNumber + 1

after this loop

        while (tempCalendar.time <= lastDateForPage && index-- > 0) {
            dateList.add(tempCalendar.time)
            tempCalendar.add(Calendar.DATE, 1)
        }
        

the loop might actually break without the limit getting reached, this means your datasource doesn't guarantee that limitDate is always reached.

Changing the condition lastDateForPage == limitDate to check on the following, should fix the issue

            val limitCalendar = Calendar.getInstance().apply { limitDate }

            if (dateList.lastOrNull()?.let { Calendar.getInstance().apply { time = it } }
                    ?.let {
                        it.get(Calendar.YEAR) == limitCalendar.get(Calendar.YEAR) &&
                                it.get(Calendar.DAY_OF_YEAR) == limitCalendar.get(Calendar.DAY_OF_YEAR)
                    } == true)
                null
            else
                pageNumber + 1
Ambidexter answered 8/1, 2022 at 21:46 Comment(7)
I tried your code it's not working it giving wrong date :(Amyamyas
Then i need you to describe the usecase you're trying to achieve, so that i can understand the problem you're facingAmbidexter
I need to show as date in my current page viewpager. If I pass date as 29/11/2021 it need to start screen with this date. But right now it taking wrong index.Amyamyas
I added new explanation in details section with YouTube link please have a look. If any doubt please let me know. ThanksAmyamyas
I cannot reproduce the issue, anyways lets assume that there's no problem with your datasource, add thislog statement Log.d("Debugging paging", "load: ${params.key} ${Throwable().stackTraceToString()}") to your ViewPagerPagingSource.load and post its outputAmbidexter
I added the output. Please have a look. ThanksAmyamyas
you need to run fast so many times in your device. Press button and go back to previous screen and so and so.Amyamyas

© 2022 - 2024 — McMap. All rights reserved.