Skip to content

Cache AsyncImage results#65

Merged
colemancda merged 2 commits into
masterfrom
feature/asyncimage-cache
Jul 24, 2026
Merged

Cache AsyncImage results#65
colemancda merged 2 commits into
masterfrom
feature/asyncimage-cache

Conversation

@colemancda

Copy link
Copy Markdown
Member

Closes the correctness gap flagged when AsyncImage landed: every re-keyed AsyncImage dropped its bitmap and re-fetched over the network — the spinner flashed on every URL change, scroll-back, or re-navigation.

Fix

A process-wide LRU cache of decoded bitmaps, keyed by URL (ImageCache, access-ordered LinkedHashMap capped at 64 entries, synchronizedMap since fetches run on Dispatchers.IO). RenderAsyncImage now seeds its state from the cache synchronously — a URL seen before renders on the first frame with no spinner — and the LaunchedEffect early-returns on a hit, so only a genuine miss touches the network. Successful fetches populate the cache; failures aren't cached (so a transient failure can retry).

Lives in commonMain alongside ImageFetch (both JVM targets), so Android and the desktop rig share it.

Verification

Kotlin-only change, so no swift test coverage — verified on-device by counting real network fetches. Temporarily logged each call to fetchImageBytes (which only runs on a cache miss), then toggled the demo's "Load the other image" button A→B→A→B→A→B (6 times):

  • before: that pattern re-fetches on every toggle
  • after: exactly 1 network fetch across all six — for the one URL not already cached from the initial load. Every return to a previously-seen URL was a cache hit with zero network traffic.

The instrumentation was removed before committing (the diff is just the cache and the two-line RenderAsyncImage change).

Scope

In-memory only — no disk cache, so it doesn't survive a process restart (fine for a session; a real app would add a disk tier). Cache is unbounded in byte size, bounded only by entry count (64).

@colemancda
colemancda merged commit f523ed6 into master Jul 24, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant