diff --git a/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.ios.kt b/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.ios.kt index 6dbde0def2152..65e96736eaa70 100644 --- a/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.ios.kt +++ b/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/scene/ComposeSceneMediator.ios.kt @@ -214,16 +214,12 @@ internal class ComposeSceneMediator( private val isActive get() = coroutineContext.isActive - private var isPrefetchVoteActive: Boolean = false // TODO CMP-10587: Move inside the IosPrefetchScheduler private val prefetchScheduler = IosPrefetchScheduler( - onHasWorkScheduled = { hasWork -> - if (hasWork != isPrefetchVoteActive) { - isPrefetchVoteActive = hasWork - if (hasWork) { - activitiesHandler.onActivitiesStarted() - } else { - activitiesHandler.onActivitiesEnded() - } + onPrefetchVoteChanged = { isVoteActive -> + if (isVoteActive) { + activitiesHandler.onActivitiesStarted() + } else { + activitiesHandler.onActivitiesEnded() } } ) diff --git a/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/window/IosPrefetchScheduler.ios.kt b/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/window/IosPrefetchScheduler.ios.kt index c2dffc12c11d1..f3f5cd78200f2 100644 --- a/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/window/IosPrefetchScheduler.ios.kt +++ b/compose/ui/ui/src/iosMain/kotlin/androidx/compose/ui/window/IosPrefetchScheduler.ios.kt @@ -28,12 +28,19 @@ import platform.QuartzCore.CACurrentMediaTime internal class IosPrefetchScheduler( private val currentTime: () -> NSTimeInterval = { CACurrentMediaTime() }, - private var onHasWorkScheduled: (Boolean) -> Unit, + private var onPrefetchVoteChanged: (Boolean) -> Unit, ) : PlatformPrefetchScheduler { private val scheduledPrefetchRequests = ScheduledPrefetchRequests() private val scope = PrefetchRequestScopeImpl() private val hasWorkScheduled: Boolean get() = scheduledPrefetchRequests.hasWorkScheduled + /** + * Whether the scheduler currently votes for prefetch activities to be running. + * + * Tracked here so [onPrefetchVoteChanged] is only invoked when the vote actually changes. + */ + private var isPrefetchVoteActive: Boolean = false + /** * Marks the start of the display-link interval where drawing happened. */ @@ -73,7 +80,7 @@ internal class IosPrefetchScheduler( } scheduledPrefetchRequests.addHighPriority(request) - onHasWorkScheduled(hasWorkScheduled) + updatePrefetchVote(hasWorkScheduled) } override fun scheduleLowPriorityPrefetch(request: PlatformPrefetchRequest) { @@ -86,7 +93,7 @@ internal class IosPrefetchScheduler( } scheduledPrefetchRequests.addLowPriority(request) - onHasWorkScheduled(hasWorkScheduled) + updatePrefetchVote(hasWorkScheduled) } /** @@ -106,7 +113,7 @@ internal class IosPrefetchScheduler( "IosPrefetchScheduler.execute() must be called on main thread" } if (isDisposed) { - onHasWorkScheduled(false) + updatePrefetchVote(false) return } @@ -119,7 +126,7 @@ internal class IosPrefetchScheduler( } if (!hasWorkScheduled) { - onHasWorkScheduled(false) + updatePrefetchVote(false) return } @@ -137,7 +144,7 @@ internal class IosPrefetchScheduler( } } - onHasWorkScheduled(hasWorkScheduled) + updatePrefetchVote(hasWorkScheduled) traceValue("compose:lazy:prefetch:available_time_nanos", 0L) } @@ -166,8 +173,15 @@ internal class IosPrefetchScheduler( } isDisposed = true scheduledPrefetchRequests.clear() - onHasWorkScheduled(false) - onHasWorkScheduled = {} + updatePrefetchVote(false) + onPrefetchVoteChanged = {} + } + + private fun updatePrefetchVote(hasWork: Boolean) { + if (hasWork != isPrefetchVoteActive) { + isPrefetchVoteActive = hasWork + onPrefetchVoteChanged(hasWork) + } } private fun executeRequest(): Boolean { diff --git a/compose/ui/ui/src/iosTest/kotlin/androidx/compose/ui/window/PlatformPrefetchSchedulerTest.kt b/compose/ui/ui/src/iosTest/kotlin/androidx/compose/ui/window/PlatformPrefetchSchedulerTest.kt index 50ee5249918b8..ef49f314a842d 100644 --- a/compose/ui/ui/src/iosTest/kotlin/androidx/compose/ui/window/PlatformPrefetchSchedulerTest.kt +++ b/compose/ui/ui/src/iosTest/kotlin/androidx/compose/ui/window/PlatformPrefetchSchedulerTest.kt @@ -91,34 +91,46 @@ class PlatformPrefetchSchedulerTest { } @Test - fun testReportsNoScheduledWorkWhenNoRequestsAreScheduledAndDidNotDraw() { - val hasWorkEvents = mutableListOf() - val scheduler = scheduler(onHasWorkScheduled = hasWorkEvents::add) + fun testDoesNotVoteForPrefetchWhenNoRequestsAreScheduledAndDidNotDraw() { + val prefetchVoteEvents = mutableListOf() + val scheduler = scheduler(onPrefetchVoteChanged = prefetchVoteEvents::add) scheduler.execute(lastFrameTimestamp = 0.0, targetTimestamp = 1.0, didDraw = false) - assertEquals(listOf(false), hasWorkEvents) + assertTrue(prefetchVoteEvents.isEmpty()) } @Test - fun testReportsNoScheduledWorkWhenNoRequestsAreScheduledAndDidDraw() { - val hasWorkEvents = mutableListOf() - val scheduler = scheduler(onHasWorkScheduled = hasWorkEvents::add) + fun testDoesNotVoteForPrefetchWhenNoRequestsAreScheduledAndDidDraw() { + val prefetchVoteEvents = mutableListOf() + val scheduler = scheduler(onPrefetchVoteChanged = prefetchVoteEvents::add) scheduler.execute(lastFrameTimestamp = 0.0, targetTimestamp = 1.0, didDraw = true) - assertEquals(listOf(false), hasWorkEvents) + assertTrue(prefetchVoteEvents.isEmpty()) } @Test - fun testReportsNoScheduledWorkWhenQueueDrains() { - val hasWorkEvents = mutableListOf() - val scheduler = scheduler(onHasWorkScheduled = hasWorkEvents::add) + fun testVotesForPrefetchWhileWorkIsScheduledAndEndsWhenQueueDrains() { + val prefetchVoteEvents = mutableListOf() + val scheduler = scheduler(onPrefetchVoteChanged = prefetchVoteEvents::add) scheduler.scheduleLowPriorityPrefetch(request("request")) scheduler.execute(lastFrameTimestamp = 0.0, targetTimestamp = 1.0, didDraw = false) - assertEquals(listOf(true, false), hasWorkEvents) + assertEquals(listOf(true, false), prefetchVoteEvents) + } + + @Test + fun testDoesNotEmitDuplicatePrefetchVotes() { + val prefetchVoteEvents = mutableListOf() + val scheduler = scheduler(onPrefetchVoteChanged = prefetchVoteEvents::add) + + scheduler.scheduleLowPriorityPrefetch(request("request-0")) + scheduler.scheduleLowPriorityPrefetch(request("request-1")) + scheduler.execute(lastFrameTimestamp = 0.0, targetTimestamp = 1.0, didDraw = false) + + assertEquals(listOf(true, false), prefetchVoteEvents) } @Test @@ -169,10 +181,10 @@ class PlatformPrefetchSchedulerTest { } private fun scheduler( - onHasWorkScheduled: (Boolean) -> Unit = {}, + onPrefetchVoteChanged: (Boolean) -> Unit = {}, currentTime: () -> NSTimeInterval = { 0.0 }, ) = IosPrefetchScheduler( - onHasWorkScheduled = onHasWorkScheduled, + onPrefetchVoteChanged = onPrefetchVoteChanged, currentTime = currentTime, )