Skip to content

Thumbnail Loading Optimization#36

Open
steven-ahfu wants to merge 1 commit intoGoodwy:mainfrom
steven-ahfu:thumb-optimization
Open

Thumbnail Loading Optimization#36
steven-ahfu wants to merge 1 commit intoGoodwy:mainfrom
steven-ahfu:thumb-optimization

Conversation

@steven-ahfu
Copy link

Thumbnail Loading Optimization

1. Robust Thumbnail Prefetching

The bulk of the optimization comes from new prefetching logic in MediaAdapter.kt and DirectoryAdapter.kt.

  • Sliding Window Lookahead: As the user scrolls, the app now calculates the first visible item and looks ahead to preload the next 48 items in MediaAdapter (and 20 items in DirectoryAdapter).
  • Memory Budgeting: It intelligently caps the MediaAdapter prefetching at either 48 items OR 24MB of estimated uncompressed bitmap memory (prefetchSizeBudgetBytes = 24L * 1024L * 1024L). This ensures aggressive preloading won't cause Out-Of-Memory (OOM) crashes on low-end devices.
  • Glide Priorities: It specifically flags the next 12 immediate items (highPriorityLookAheadItems) with Glide's Priority.HIGH, while the rest get Priority.NORMAL. This ensures the images the user is about to see right now are prioritized over images further down the scroll buffer.
  • Low-Res Placeholders: It adds a .thumbnail(0.1f) directive to the Glide builder inside loadImageBase. This means while the full quality thumbnail is loading, Glide will display a 10% resolution placeholder instantly.

2. "Show All" View Optimization (I/O Bottleneck Fix)

The "Show all folders" view in Android 11+ (Scoped Storage) is notoriously slow because it has to heavily query the MediaStore.

  • Upfront Querying: In GetMediaAsynctask.kt, when showAll is true, it now performs a single, upfront query via getAndroid11FolderMedia().
  • Passing the Cache Down: It passes this heavily queried result down as prefetchedAndroid11Files to mediaFetcher.getFilesFrom(). This avoids each individual folder scan having to redundantly hit the I/O layer.
  • Database Maintenance: Added maybeRunMediaDbMaintenance() which silently runs in the background on a 6-hour interval to clean up stale and deleted rows in the database in batches of 100. A smaller, cleaner Room DB means much faster disk queries.

3. Smart App Memory Management

Tightly couples Glide's memory behaviors with the Android Application Lifecycle:

  • Background/Foreground Throttling: App.kt was modified to listen to onTrimMemory. When the UI is hidden (the app is in the background), Glide's memory category is dropped to LOW. When memory is critically low (onLowMemory), the cache is wiped.
  • Resuming: When the user returns to the app, SimpleActivity.onResume bumps the memory category back to NORMAL. This frees up RAM when the user isn't using the app, but maximizes caching when they are.

4. User-Configurable Cache Setting

  • UI Integration: A new option "Thumbnail cache size" was added to SettingsActivity using a Radio Button dialog.
  • Scaling: Users can choose between 100 MB, 256 MB, 512 MB, 1024 MB, and 2048 MB. The default is set to 512 MB.
  • Glide Configuration: In SvgModule.kt (which serves as the AppGlideModule), the app reads this exact Config state and dynamically applies it to Glide's InternalCacheDiskCacheFactory. It also expanded the RAM allowance to keep exactly 3 screens worth of thumbnails in active memory (setMemoryCacheScreens(3f)).

The scroll performance should be significantly smoother due to the budgeted prefetching, the showAll query will be unblocked on Android 11+, and the user now has direct control over how much storage the app uses for caching.

@steven-ahfu steven-ahfu marked this pull request as draft March 9, 2026 04:22
@steven-ahfu steven-ahfu force-pushed the thumb-optimization branch 4 times, most recently from bcc9dee to b90703c Compare March 9, 2026 12:46
@steven-ahfu
Copy link
Author

Updates

Context.kt (loadImageBase):

  • Added thumbnailSize: Int = 0 parameter
  • When > 0, calls options.override(thumbnailSize) — tells Glide to downsample during decode instead of decoding full-res then shrinking
  • Removed .thumbnail(0.1f) — no more double-loading
  • Removed skipThumbnail param (now unused)
  • Removed WebpBitmapFactory.sUseSystemDecoder = false per-call

Context.kt (loadImage):

  • Added thumbnailSize: Int = 0 parameter, passes through to loadImageBase

Context.kt (preloadImageBase):

  • Added thumbnailSize: Int = 0 parameter with .override()
  • Added .dontAnimate() + .decode(Bitmap::class.java) — cache keys now match loadImageBase, preventing double-decode
  • Removed per-call WebpBitmapFactory.sUseSystemDecoder

MediaAdapter.kt:

  • Added cachedThumbnailSize field, set from the measured child view in prefetchVisibleRangeThumbnails
  • Both setupThumbnail and prefetchVisibleRangeThumbnails now pass the size through

SvgModule.kt:

  • WebpBitmapFactory.sUseSystemDecoder = false set once at Glide init

The cachedThumbnailSize defaults to 0 until the first prefetch measures a child view, so the very first few binds before
layout will fall back to Glide's normal view-size detection — no regression there.

@steven-ahfu steven-ahfu marked this pull request as ready for review March 9, 2026 13:04
@steven-ahfu
Copy link
Author

This work has significantly improved the app experience for me. Theres no test in the repo, but pull down these changes build and test locally in a large library. you will notice how much snappier everything loads immediately.

@steven-ahfu
Copy link
Author

noticed small bugs when:

  • selecting images while thumbs are async loading
  • managing excluded folders doesn't remove folder from folder view.

will fix and update PR.

@steven-ahfu steven-ahfu marked this pull request as draft March 9, 2026 14:06
@Goodwy
Copy link
Owner

Goodwy commented Mar 10, 2026

Hello. Thank you. I tested it on an emulator and on a real device. Media file thumbnails load faster, but folder thumbnails load very slowly, with some taking several seconds to appear.

@steven-ahfu
Copy link
Author

steven-ahfu commented Mar 10, 2026

@Goodwy

thanks for checking it out!

hmm i have it installed on two devices and using because its such a huge improvement for me and I'm not encountering that behavior. the largest album i have has like 500+ photos/videos.

did you pull the latest commit? cache size? and was that behavior even after the initial load?

Edit: ohh you mean the photo thumbs of the actual folder itself? yes i noticed that a compressed version first appears and then will eventually render the higher quality thumb. let me make sure I'm prioritizing caching of that folder thumb.

if subsequent loads are caching correctly is the initial behavior okay with you or how would you like it to be changed?

@Goodwy
Copy link
Owner

Goodwy commented Mar 10, 2026

I made a screen recording from the emulator. The first app with the black theme is your changes, and the second white one is without your changes. Folder thumbnails take a long time to load after unloading the app from memory. It's faster on a physical device, but still much slower than in the old version.

Another problem appeared on the emulator that is not present on my physical device: when updating media files, the files get mixed up. I haven't figured out why this is happening yet.

https://www.youtube.com/watch?v=E2DskIgBpGA

@steven-ahfu steven-ahfu marked this pull request as ready for review March 12, 2026 22:00
@steven-ahfu
Copy link
Author

@Goodwy updated the PR, issues should be fixed.

Prioritized caching / retrieval of folder thumbs and the slow low res step. on fresh app cold start latency is expected to be slow. After that, the app should be loading gallery thumbs much much faster.

I also updated behavior around built in photo editor. when new photo is created or existing media is over written it should trigger a gallery thumb refresh instead of having to manually do so.

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.

2 participants