Skip to content

Commit 5675330

Browse files
evantcopybara-github
authored andcommitted
[GH] Don't drop retry/refresh events before uiReceiver is set
## Proposed Changes Because uiReceiver is set asynchronously it's possible to race with a call to retry or refresh on the PagingDataPresenter. Changed the implementation to record calling these methods and then forward the event when the uiReceiver is set. ## Testing Test: ./gradlew test connectedCheck ## Issues Fixed Fixes: b/330725007 This is an imported pull request from #754. Resolves #754 Github-Pr-Head-Sha: b2603e5 GitOrigin-RevId: 6cf6538 Change-Id: I46f7b0538108a070ca417f44113d723610b541ba
1 parent 34cdcf1 commit 5675330

2 files changed

Lines changed: 61 additions & 5 deletions

File tree

paging/paging-common/src/commonMain/kotlin/androidx/paging/PagingDataPresenter.kt

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ public abstract class PagingDataPresenter<T : Any>(
6868
cachedPagingData: PagingData<T>? = null,
6969
) {
7070
private var hintReceiver: HintReceiver? = null
71-
private var uiReceiver: UiReceiver? = null
71+
private var uiReceiver: UiReceiver = InitialUiReceiver()
7272
private var pageStore: PageStore<T> = PageStore.initial(cachedPagingData?.cachedEvent())
7373
private val combinedLoadStatesCollection =
7474
MutableCombinedLoadStateCollection().apply {
@@ -114,7 +114,7 @@ public abstract class PagingDataPresenter<T : Any>(
114114

115115
public suspend fun collectFrom(pagingData: PagingData<T>): @JvmSuppressWildcards Unit {
116116
collectFromRunner.runInIsolation {
117-
uiReceiver = pagingData.uiReceiver
117+
setUiReceiver(pagingData.uiReceiver)
118118
pagingData.flow.collect { event ->
119119
log(VERBOSE) { "Collected $event" }
120120
withContext(mainContext) {
@@ -309,7 +309,7 @@ public abstract class PagingDataPresenter<T : Any>(
309309
*/
310310
public fun retry() {
311311
log(DEBUG) { "Retry signal received" }
312-
uiReceiver?.retry()
312+
uiReceiver.retry()
313313
}
314314

315315
/**
@@ -329,7 +329,7 @@ public abstract class PagingDataPresenter<T : Any>(
329329
*/
330330
public fun refresh() {
331331
log(DEBUG) { "Refresh signal received" }
332-
uiReceiver?.refresh()
332+
uiReceiver.refresh()
333333
}
334334

335335
/** @return Total number of presented items, including placeholders. */
@@ -500,6 +500,33 @@ public abstract class PagingDataPresenter<T : Any>(
500500
hintReceiver?.accessHint(newPageStore.initializeHint())
501501
}
502502
}
503+
504+
// Holds on to retry/refresh requests to deliver them when the real UiReceiver is attached.
505+
private class InitialUiReceiver : UiReceiver {
506+
var retry = false
507+
var refresh = false
508+
509+
override fun retry() {
510+
retry = true
511+
}
512+
513+
override fun refresh() {
514+
refresh = true
515+
}
516+
}
517+
518+
private fun setUiReceiver(receiver: UiReceiver) {
519+
val oldReceiver = this.uiReceiver
520+
this.uiReceiver = receiver
521+
if (oldReceiver is InitialUiReceiver) {
522+
if (oldReceiver.retry) {
523+
receiver.retry()
524+
}
525+
if (oldReceiver.refresh) {
526+
receiver.refresh()
527+
}
528+
}
529+
}
503530
}
504531

505532
/**

paging/paging-common/src/commonTest/kotlin/androidx/paging/PagingDataPresenterTest.kt

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,6 +138,36 @@ class PagingDataPresenterTest {
138138
job.cancel()
139139
}
140140

141+
@Test
142+
fun retrySentBeforeCollection() =
143+
testScope.run {
144+
val presenter = SimplePresenter()
145+
val receiver = UiReceiverFake()
146+
147+
presenter.retry()
148+
149+
val job = launch { presenter.collectFrom(infinitelySuspendingPagingData(receiver)) }
150+
151+
assertEquals(1, receiver.retryEvents.size)
152+
153+
job.cancel()
154+
}
155+
156+
@Test
157+
fun refreshSentBeforeCollection() =
158+
testScope.runTest {
159+
val presenter = SimplePresenter()
160+
val receiver = UiReceiverFake()
161+
162+
presenter.refresh()
163+
164+
val job = launch { presenter.collectFrom(infinitelySuspendingPagingData(receiver)) }
165+
166+
assertEquals(1, receiver.refreshEvents.size)
167+
168+
job.cancel()
169+
}
170+
141171
@Test
142172
fun uiReceiverSetImmediately() =
143173
testScope.runTest {
@@ -2471,7 +2501,6 @@ class PagingDataPresenterTest {
24712501
val job1 = launch {
24722502
presenter.collectFrom(PagingData(flow, uiReceiver2, dummyHintReceiver))
24732503
}
2474-
presenter.refresh()
24752504
assertThat(uiReceiver.refreshEvents).hasSize(0)
24762505
assertThat(uiReceiver2.refreshEvents).hasSize(1)
24772506
job1.cancel()

0 commit comments

Comments
 (0)