Status Update
Comments
no...@gmail.com <no...@gmail.com> #2
Branch: androidx-master-dev
commit b90079595f33f58fece04026a97faa0d243acdb1
Author: Yuichi Araki <yaraki@google.com>
Date: Wed Sep 18 16:55:49 2019
Change the way to detect mismatch between POJO and query
This fixes cursor mismatch warnings with expandProjection.
Bug: 140759491
Test: QueryMethodProcessorTest
Change-Id: I7659002e5e0d1ef60fc1af2a625c0c36da0664d8
M room/compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
M room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
M room/compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
M room/compiler/src/test/kotlin/androidx/room/processor/QueryMethodProcessorTest.kt
M room/compiler/src/test/kotlin/androidx/room/testing/TestProcessor.kt
du...@google.com <du...@google.com> #3
b9...@gmail.com <b9...@gmail.com> #4
Branch: androidx-master-dev
commit bdde5a1a970ddc9007b28de4aa29d60ffa588f08
Author: Yigit Boyar <yboyar@google.com>
Date: Thu Apr 16 16:47:05 2020
Re-factor how errors are dismissed when query is re-written
This CL changes how we handle errors/warnings if query is
re-written.
There was a bug in expandProjection where we would report warnings
for things that Room already fixes automatically (
The solution to that problem (I7659002e5e0d1ef60fc1af2a625c0c36da0664d8)
solved it by deferring validating of columns until after re-write
decision is made. Unfortunately, this required changing PojoRowAdapter
to have a dummy mapping until it is validating, make it hard to use
as it does have a non-null mapping which is not useful.
This CL partially reverts that change and instead rely on the log
deferring logic we have in Context. This way, we don't need to break
the stability of PojoRowAdapter while still having the ability to
drop warnings that room fixes. This will also play nicer when we
have different query re-writing options that can use more information
about the query results.
Bug: 153387066
Bug: 140759491
Test: existing tests pass
Change-Id: I2ec967c763d33d7a3ff02c1a13c6953b460d1e5f
M room/compiler/src/main/kotlin/androidx/room/log/RLog.kt
M room/compiler/src/main/kotlin/androidx/room/processor/QueryMethodProcessor.kt
M room/compiler/src/main/kotlin/androidx/room/solver/TypeAdapterStore.kt
M room/compiler/src/main/kotlin/androidx/room/solver/query/result/PojoRowAdapter.kt
du...@google.com <du...@google.com> #5
A few notes:
- Should be using
viewLifecyclerOwner.lifecycleScope
instead of justlifecycleScope
in aFragment
. - Cancellation is happening as a result of the fragment automatically paging over when they are initially created / added to viewpager2 (this is expected as far as I can tell)
- Right now
RemoteMediator
kind of acts as a dumb callback wheneverPagingSource
runs out of items to load, soREFRESH
error doesn't prevent remotePREPEND
/APPEND
. It's not clear to me yet whether it should or if we should change guidance to just handleAPPEND
. This is due to us changing the behavior of Remote REFRESH after the codelab was produced, so I'm very sorry for that. REFRESH is meant to handle any init that needs to happen, technically the initial network page fetch happens as a result of running out of items on the APPEND side.
e.g., Here is a modified sample from the opening post / #1's repro project that fixes the issue:
PageKeyedRemoteMediator.kt
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, RedditPost>
): MediatorResult {
try {
// Get the closest item from PagingState that we want to load data around.
val loadKey = when (loadType) {
REFRESH -> {
db.withTransaction {
postDao.deleteBySubreddit(subredditName)
remoteKeyDao.deleteBySubreddit(subredditName)
}
return MediatorResult.Success(endOfPaginationReached = false)
}
PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
APPEND -> {
// Query DB for SubredditRemoteKey for the subreddit.
// SubredditRemoteKey is a wrapper object we use to keep track of page keys we
// receive from the Reddit API to fetch the next or previous page.
val remoteKey = db.withTransaction {
remoteKeyDao.remoteKeyByPost(subredditName)
}
when {
remoteKey == null -> null
// We must explicitly check if the page key is null after initial load, since the
// Reddit API informs the end of the list by returning null for page key, but
// passing a null key to Reddit API will fetch the initial page.
remoteKey.nextPageKey == null -> {
return MediatorResult.Success(endOfPaginationReached = true)
}
else -> remoteKey.nextPageKey
}
}
}
val data = redditApi.getTop(
subreddit = subredditName,
after = loadKey,
before = null,
limit = state.config.pageSize * 3
).data
val items = data.children.map { it.data }
db.withTransaction {
remoteKeyDao.insert(SubredditRemoteKey(subredditName, data.after))
postDao.insertAll(items)
}
return MediatorResult.Success(endOfPaginationReached = items.isEmpty())
} catch (e: IOException) {
return MediatorResult.Error(e)
} catch (e: HttpException) {
return MediatorResult.Error(e)
} catch (e: CancellationException) {
Log.d("RemoteMediator","$subredditName $loadType canceled")
throw e
}
}
du...@google.com <du...@google.com> #6
Will keep this bug open to track the decision on blocking RemoteMediator
PREPEND
/ APPEND
after remote REFRESH error and any possible updates to the Codelab / Github samples.
The inconvenience of blocking on REFRESH is going to surface via needing to call retry()
, but it's possible there's something better we can do to handle cancellation more gracefully since we don't want to keep creating new PagingData
and also support loading in the background. (the thing to change to control this is when you start collecting from Pager.flow
, and the scope collection happens in).
b9...@gmail.com <b9...@gmail.com> #7
I have a question about the modified sample. Shouldn't deleteByXXX
be moved to after redditApi
execute successfully?
The UI side will see blink twice in this sample: Show db cache first then disappeared, show data again after redditApi
successful and insert data. The user experience is pretty bad.
du...@google.com <du...@google.com> #8
To be clear the above sample is a workaround a deeper issue in Paging which isn't handling cancellation gracefully due to Remote REFRESH errors not blocking remote PREPEND / APPEND.
b9...@gmail.com <b9...@gmail.com> #9
I found that if using fragment.lifecycleScope.launchWhenResume to collect each page data then it will work normally. ViewPager2 FragmentStatePager will move fragment to onResume/onPause.
du...@google.com <du...@google.com> #10
Good to hear, I think we can still do a better job of handling cancellation and propagating remote state, e.g., if you have many pages in viewpager and flip back and forth I think you'll still eventually get stuck, since the collector on PagingData will get canceled and there's no invalidate signal to restart it without manually calling adapter.refresh()
.
yb...@google.com <yb...@google.com>
ap...@google.com <ap...@google.com> #11
Branch: androidx-master-dev
commit c97e21810e49b3f8043c8d7c3951b16e8b38e6db
Author: Yigit Boyar <yboyar@google.com>
Date: Fri Oct 16 10:39:22 2020
Add priority to single runner
This CL adds ability to accept priority to SingleRunner.
It will be necessary for the new remote mediator implementation
where it will rely on single runner to cancel lower priority
requests.
Bug: 162252536
Test: SingleRunnerTest
Change-Id: I01351ac385b252f80ab6079d42f2cf0edf8d6667
M paging/common/src/main/kotlin/androidx/paging/SingleRunner.kt
M paging/common/src/test/kotlin/androidx/paging/SingleRunnerTest.kt
ap...@google.com <ap...@google.com> #12
Branch: androidx-master-dev
commit f86129d60bb33243d06837291f0e2bd0f26f4bd3
Author: Yigit Boyar <yboyar@google.com>
Date: Fri Oct 09 11:34:07 2020
RemoteMediatorAccessor Refactor
This CL changes how remote mediator and snapshots interact.
Previously, all calls to remote mediator were done in a
snapshot's scope and snapshot was also responsible to track
load states.
It created a problem where remote mediator calls would be
cancelled when snapshot is replaced by a newer one. It also
made the load state logic fairly complicated in PageFetcherSnapshot.
This CL de-couples the two as much as possible.
With this change, RemoteMediatorAcccessor uses the scope of
the `Pager.flow` collection instead of the scope from the
PageFetcherSnapshot. PageFetcherSnapshot is now only responsible
to send events to the accessor when it needs more data.
RemoteMediatorAccessor manages its own loading state.
We still need some syncronization between the two to ensure certain
load events are not dispatched before data is loaded (e.g. after a
pull to refresh, you don't want refresh state to go idle before the
new local data is loaded for UI consistency). This logic is now
handled by the PageFetcher where it appends the load states to each
insert page and for other events (loading, error), directly sends them
to the downstream.
Bug: 162252536
Test: RemoteMediatorAccessorTest, PageFetcherSnapshotTest
Relnote: "Paging will no longer cancel a `RemoteMediator#load` call
due to a page invalidation. It will also no longer make an
append/prepend load request, *if REFRESH is required*, until REFRESH
request completes successfully."
Change-Id: I6390be0c0c1073005456f928a2a8afa81c16d3ef
M paging/common/api/current.txt
M paging/common/api/public_plus_experimental_current.txt
M paging/common/api/restricted_current.txt
M paging/common/src/main/kotlin/androidx/paging/CachedPageEventFlow.kt
M paging/common/src/main/kotlin/androidx/paging/LoadStates.kt
M paging/common/src/main/kotlin/androidx/paging/MutableLoadStateCollection.kt
M paging/common/src/main/kotlin/androidx/paging/PageEvent.kt
M paging/common/src/main/kotlin/androidx/paging/PageFetcher.kt
M paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshot.kt
M paging/common/src/main/kotlin/androidx/paging/PageFetcherSnapshotState.kt
M paging/common/src/main/kotlin/androidx/paging/PagingDataDiffer.kt
M paging/common/src/main/kotlin/androidx/paging/RemoteMediator.kt
M paging/common/src/main/kotlin/androidx/paging/RemoteMediatorAccessor.kt
M paging/common/src/test/kotlin/androidx/paging/PageEventTest.kt
M paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotStateTest.kt
M paging/common/src/test/kotlin/androidx/paging/PageFetcherSnapshotTest.kt
M paging/common/src/test/kotlin/androidx/paging/RemoteMediatorAccessorTest.kt
M testutils/testutils-paging/src/main/java/androidx/paging/RemoteMediatorMock.kt
Description
Version used: Paging-3.0.0-alpha03, ViewPager2-1.0.0, Material-1.2.0-rc01
Devices/Android versions reproduced on: Samsung M40(SM-M405F) running Android 10
I am using PagingDataAdapter for my recyclerview in the fragment inside a viewpager. I am following Page from network and database as instructed in
Whenever I load my fragment, load() function with loadtype REFRESH triggers but in the middle of it, the suspend function gets cancelled. I am unable to find out which function is calling cancel(). Even though loadtype REFRESH get cancelled, subsequent PREPEND and APPEND loads are not cancelled. And its causing me a issue that remote keys are not stored in the database when in REFRESH.
And this issue is reproducing only in alpha03 not in alpha02.
I attached the sample project with this issue, Actually its an update over the PagingWithNetwork Sample with a new activity for ViewPager. Checkout the implementation in PagerActivity.
PagerActivity calls ViewPagerFragment onCreate() which contains TabLayout and ViewPager2. And I added two fragments with different subredditName for demo purpose, first is with subreddit "androiddev" and later is "Kotlin". I added delay and yield for only "androiddev" in RemoteMediator to demonstrate cancelling of suspend function.