feat(android): add HomeArtistPrewarmer (top-8 artists, session-deduped)
This commit is contained in:
@@ -0,0 +1,33 @@
|
|||||||
|
package com.fabledsword.minstrel.metadata
|
||||||
|
|
||||||
|
import java.util.Collections
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
private const val TOP_N = 8
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-warms the Room cache for likely-tap artist tiles on Home. Mirrors
|
||||||
|
* Flutter's `MetadataPrefetcher`: conservative — artists only (a single
|
||||||
|
* round-trip per id), top-8 per call, and once per id per session so
|
||||||
|
* repeated index refreshes don't re-fetch. Albums are intentionally not
|
||||||
|
* pre-warmed (their detail fetch fans out a track list).
|
||||||
|
*
|
||||||
|
* Fetches go through [MetadataProvider.refreshArtist], so they share the
|
||||||
|
* same per-id dedup AND the 4-slot concurrency gate as on-render
|
||||||
|
* hydration — the prewarm can't starve the tiles the user is looking at.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class HomeArtistPrewarmer @Inject constructor(
|
||||||
|
private val metadataProvider: MetadataProvider,
|
||||||
|
) {
|
||||||
|
private val warmed = Collections.synchronizedSet(mutableSetOf<String>())
|
||||||
|
|
||||||
|
fun warm(artistIds: List<String>) {
|
||||||
|
artistIds.asSequence()
|
||||||
|
.filter { it.isNotEmpty() }
|
||||||
|
.filter { warmed.add(it) }
|
||||||
|
.take(TOP_N)
|
||||||
|
.forEach { metadataProvider.refreshArtist(it) }
|
||||||
|
}
|
||||||
|
}
|
||||||
+25
@@ -0,0 +1,25 @@
|
|||||||
|
package com.fabledsword.minstrel.metadata
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
|
||||||
|
class HomeArtistPrewarmerTest {
|
||||||
|
private val provider = mockk<MetadataProvider>()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `warms at most the top eight distinct ids, once per session`() {
|
||||||
|
every { provider.refreshArtist(any()) } returns mockk(relaxed = true)
|
||||||
|
val prewarmer = HomeArtistPrewarmer(provider)
|
||||||
|
|
||||||
|
// 10 ids with a duplicate and a blank -> 8 distinct non-blank warmed.
|
||||||
|
prewarmer.warm(listOf("a1", "a2", "a3", "a4", "a5", "a6", "a7", "a8", "a9", ""))
|
||||||
|
// Re-warm: already-warmed ids are skipped.
|
||||||
|
prewarmer.warm(listOf("a1", "a2"))
|
||||||
|
|
||||||
|
(1..8).forEach { i -> verify(exactly = 1) { provider.refreshArtist("a$i") } }
|
||||||
|
verify(exactly = 0) { provider.refreshArtist("a9") } // past the top-8 cap
|
||||||
|
verify(exactly = 0) { provider.refreshArtist("") } // blank skipped
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user