Skip to content

Conversation

Copy link
Contributor

Copilot AI commented Dec 21, 2025

Loading local images with file:// URIs crashed with IllegalArgumentException: Expected URL scheme 'http' or 'https' but was 'file'. The code unconditionally wrapped all URIs in CustomGlideUrl, which uses OkHttp that only supports HTTP/HTTPS schemes.

Additionally, fixed evictAllForId to properly evict from both disk and memory cache as intended, fixed isInDiskCacheAsync to correctly detect local file presence in Glide's cache, implemented efficient key tracking in ModelSignatureDiskLruCacheWrapper for smart cache management, and added forceReloadImage() method to handle stale cache issues when image content changes.

Changes

  • Added isNetworkUri() helper to detect HTTP/HTTPS vs local URIs
  • Conditional model creation - use CustomGlideUrl only for network requests needing headers/callbacks, plain strings for local files
  • Applied consistently in both loadImageWithGlide() and prefetchToCache()
  • Optimized HashMap allocation to only when headers exist
  • Fixed evictAllForId() to properly evict from both disk (source + transformed) and memory cache with smart strategy selection
  • Fixed isInDiskCacheAsync() to correctly detect local file:// URIs by checking Glide's RESOURCE_DISK_CACHE using ResourceCacheKey
  • Auto-detect decoded resource class in SaveKeysRequestListener to enable proper cache key generation
  • Implemented efficient key tracking in ModelSignatureDiskLruCacheWrapper with O(1) lookup, bulk eviction, and signature version management
  • Added forceReloadImage() method to bypass memory cache and reload changed image content

Cache Eviction Fix

The evictAllForId method now correctly:

  1. Detects if enhanced disk cache (ModelSignatureDiskLruCacheWrapper) is available
  2. Uses efficient model/signature-based bulk eviction when possible (removes ALL matching entries including transforms)
  3. Falls back to key-by-key eviction for standard disk caches
  4. Evicts from both disk cache (source + transformed data) and memory cache
  5. Combines results and reports success only if both operations succeed

This ensures images and all transforms are fully removed from both disk and memory as the method name implies.

Local File Cache Detection Fix

The isInDiskCacheAsync and isInDiskCacheBlocking methods now correctly handle local file:// URIs:

Problem: Local files are cached by Glide as decoded resources in RESOURCE_DISK_CACHE, not as source data. The code was incorrectly checking with CustomDataCacheKey for source data (which doesn't exist for local files) and missing the decodedResourceClass needed to check ResourceCacheKey.

Solution:

  1. Auto-detect decoded resource class: SaveKeysRequestListener now automatically detects the resource class from the actual decoded resource (e.g., BitmapDrawable) if not provided
  2. Skip source check for local files: For local file:// URIs (identified by ObjectKey sourceKey), skip CustomDataCacheKey check since Glide doesn't cache local file bytes
  3. Check ResourceCacheKey: Use RecreatedResourceKey to check for the decoded/cached bitmap in RESOURCE_DISK_CACHE
  4. Report correctly: For local files, if the resource is found in cache, report as sourcePresent since that IS the cached version

This ensures that:

  • isInDiskCacheAsync returns true immediately after first load for local files
  • Local files are correctly detected in Glide's RESOURCE_DISK_CACHE
  • Network URLs continue to check both source data cache and resource cache as before

Enhanced Disk Cache with Efficient Key Tracking

Implemented comprehensive key tracking system in ModelSignatureDiskLruCacheWrapper, similar to ModelSignatureMemoryCache:

Key Features:

  • Efficient O(1) lookups: ConcurrentHashMap index (safeKey → key string) for fast queries without iterating entire cache
  • Model/signature-based queries: Check existence and evict based on model/signature pairs
  • Bulk eviction: Remove all entries (raw + transformed) matching criteria in one operation
  • Signature version management: Method to remove all entries except current signature version (e.g., migrate from v1 to v2)
  • Thread-safe operations: All operations use proper synchronization
  • Comprehensive logging: Track all cache operations for debugging

New Methods in ModelSignatureDiskLruCacheWrapper:

  1. containsByModelAndSignature(model, signature) - Check if entries exist without cache iteration
  2. removeByModelAndSignature(model, signature) - Remove all matching entries (raw + transformed)
  3. removeAllExceptSignature(currentSignature) - Signature version migration support
  4. getStats() - Returns cache statistics [indexSize, diskCacheSize]

New Methods in EvictionManager:

  1. evictDiskByModelAndSignature() - Use efficient disk cache eviction when available
  2. evictDiskExceptSignature() - Remove entries with different signature versions
  3. containsInDiskCacheByModelAndSignature() - Efficient existence check
  4. getDiskCacheStats() - Get cache statistics

Performance Benefits:

  • No need to iterate entire disk cache for eviction operations
  • Fast lookups using hash-based index
  • Designed to handle large caches efficiently (many more items than memory cache)
  • Minimal overhead on normal cache operations
  • Automatic optimization when enhanced cache is available

Force Reload with Cache Bypass

Added forceReloadImage() method to handle scenarios where image file content changes but the URI remains the same:

Problem: When reloading an image after modifying its content locally:

  1. Evict from cache
  2. Reload image
  3. Glide puts the old drawable back in memory cache during eviction
  4. New load retrieves stale version from memory cache

Solution:

  • Added skipMemoryCacheOnNextLoad flag that bypasses memory cache for a single load
  • New forceReloadImage() method that:
    1. Sets flag to skip memory cache on next load
    2. Evicts from all caches (disk + memory)
    3. Clears current image and reloads
    4. Flag automatically resets after use

Usage:

// When image file content changes but URI remains the same
await imageView.forceReloadImage();

Key Features:

  • Bypasses memory cache for the reload
  • Automatically resets flag after use
  • Evicts from all caches before reload
  • Prevents Glide from serving stale cached content

Cache Key Compatibility

SaveKeysRequestListener handles both model types correctly:

  • Network with CustomGlideUrl → uses GlideUrl cache key for source, ResourceCacheKey for decoded resources
  • Local with string → uses ObjectKey(uri) as sourceKey, ResourceCacheKey for decoded resources

Both work with EvictionManager operations and the enhanced disk cache tracking system. Same URI always uses same model type.

// Before: All URIs wrapped unconditionally
loadModel = new CustomGlideUrl(uri, headersMap, ...);

// After: Conditional based on URI type
if (isNetworkUri(uri) && (headers || callbacks)) {
    loadModel = new CustomGlideUrl(uri, headersMap, ...);
} else {
    loadModel = uri;  // Let Glide use default loaders
}

Supported URI Types

  • file:// → string → Glide default file loader (fixed loading, cache detection, efficient eviction, and force reload)
  • android.resource:// → string → Glide default resource loader
  • http://, https:// → CustomGlideUrl (when headers/callbacks) or string
  • All types support efficient bulk eviction by model/signature when using enhanced disk cache
  • All types support force reload when content changes
Original prompt

i have an issue remaining in srsc/image/index.android.ts. If i try to load a local image with file:// i get this error:
java.lang.IllegalArgumentException(Expected URL scheme 'http' or 'https' but was 'file')
call GlideException#logRootCauses(String) for more detail file:///storage/0000-0000/Android/data/com.akylas.documentscanner/files/data/1766044849688/1766044849913_0/image.jpg

It seems that it comes from using CustomGlideUrl for local file://

Can you fix it while making sure the logic works for cache storing/eviction handling. EvictionManager, CustomDataFetcher, SaveKeysRequestListener, SharedPrefCacheKeyStore,... seems to be pretty "linked" to CustomGlideUrl


💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI changed the title [WIP] Fix image loading issue in CustomGlideUrl for local files Fix file:// URL handling by avoiding CustomGlideUrl for local files Dec 21, 2025
Copilot AI requested a review from farfromrefug December 21, 2025 11:14
@farfromrefug
Copy link
Member

@copilot evictAllForId should remove image and all "transforms" from disk and memory

Copy link
Contributor Author

Copilot AI commented Dec 21, 2025

@copilot evictAllForId should remove image and all "transforms" from disk and memory

Fixed in 359fe41. The method now properly evicts from both disk (source + transformed) and memory cache. After disk eviction completes, it calls removeByModelAndSignature on the memory cache, then combines the results before invoking the callback.

Copilot AI changed the title Fix file:// URL handling by avoiding CustomGlideUrl for local files Fix file:// URL handling and cache eviction in Android image loader Dec 21, 2025
@farfromrefug
Copy link
Member

@copilot i have made some improvements around local image caching and eviction. But there is still an issue i cant figure out.
If load an image into Img view from file:// the image is loaded (no transform). However isInDiskCacheAsync returns false. Now if i preload that same file:// in cache, still returns false. But then if load it again in the Img view it returns true.
This is reproducible. It should return true from the first Img loaded or after the first preload to disk cache.
I checked the sourceKey and it is ObjectKey{object=file:///.....} which i think is correct.

Try to figure it out

Copy link
Contributor Author

Copilot AI commented Dec 22, 2025

@copilot i have made some improvements around local image caching and eviction. But there is still an issue i cant figure out.
If load an image into Img view from file:// the image is loaded (no tran...

Fixed in 4b5d0d0. The issue was that Glide doesn't copy local files to its disk cache (they're already on disk), so checking with CustomDataCacheKey always returned false. Now for file:// URIs with ObjectKey sourceKey, we check if the file exists on the filesystem using java.io.File.exists() instead of checking Glide's cache. This applies to both isInDiskCacheAsync() and isInDiskCacheBlocking().

Copilot AI changed the title Fix file:// URL handling and cache eviction in Android image loader Fix file:// URL handling, cache eviction, and cache detection in Android image loader Dec 22, 2025
@farfromrefug
Copy link
Member

@copilot this is not true. On the first run i can seem image loaded from "LOCAL" but if i run the app again it is then loaded form RESOURCE_DISK_CACHE so glide do cache it.

So i need a way to evict it from cache. So first rever your last changes about File.exists
Looking at glide repo, it actually uses ResourceCacheKey for local files.
Maybe the easiest way is to do it like we do with ModelSignatureMemoryCache and create a custom DiskCache implementation which store the keys for easy query based on model/signature.

…ct decoded resource class

Co-authored-by: farfromrefug <[email protected]>
Copy link
Contributor Author

Copilot AI commented Dec 23, 2025

@copilot this is not true. On the first run i can seem image loaded from "LOCAL" but if i run the app again it is then loaded form RESOURCE_DISK_CACHE so glide do cache it.

So i need a way to evict...

Fixed in cff41c8. Reverted the File.exists() approach. Now correctly checks Glide's RESOURCE_DISK_CACHE using RecreatedResourceKey for local files. Key changes:

  1. Auto-detect decodedResourceClass from actual resource in SaveKeysRequestListener (was null before)
  2. Skip CustomDataCacheKey check for local files (ObjectKey sourceKey) - only checks ResourceCacheKey
  3. For local files, if resource is cached, report as sourcePresent since that IS the cached version

Local files are now properly detected in cache after first load.

@farfromrefug
Copy link
Member

@copilot so i added a new ModelSignatureDiskLruCacheWrapper class to use a custom DiskCache for better handling of eviction.
So implement the same technique as with ModelSignatureMemoryCache for storing cache key, query existence, and eviction (raw images and transformed images). For now eviction should remove all raw and transformed.
Try to make the key storing/querying smarter and more efficient. Their might be much more items than in the memory cache so it should fast and efficient to query/find keys. Also add a method to remove all based on a signature change. Like if i decide to go to signature v2 i should have a easy way to remove all "other" signatures.

Copy link
Contributor Author

Copilot AI commented Dec 23, 2025

@copilot so i added a new ModelSignatureDiskLruCacheWrapper class to use a custom DiskCache for better handling of eviction.
So implement the same technique as with ModelSignatureMemoryCache for stori...

Implemented in 5d7da0a. Key improvements:

ModelSignatureDiskLruCacheWrapper:

  • Efficient ConcurrentHashMap index (safeKey → key string) for O(1) lookups
  • containsByModelAndSignature() - Fast existence check without cache iteration
  • removeByModelAndSignature() - Removes all matching entries (raw + transformed)
  • removeAllExceptSignature() - For signature version migration (e.g., v1 → v2)
  • getStats() - Returns [indexSize, diskCacheSize]

EvictionManager Integration:

  • evictDiskByModelAndSignature() - Uses efficient disk cache method
  • evictDiskExceptSignature() - Bulk remove by signature version
  • containsInDiskCacheByModelAndSignature() - Efficient query
  • getDiskCacheStats() - Get statistics
  • evictAllForId() - Automatically uses efficient method when available, falls back to key-by-key

All operations are thread-safe with comprehensive logging.

Copilot AI changed the title Fix file:// URL handling, cache eviction, and cache detection in Android image loader Fix file:// URL handling, cache eviction, and cache detection with efficient disk cache tracking in Android image loader Dec 23, 2025
@farfromrefug
Copy link
Member

@copilot ok i made some changes and almost everything works. Now i have one last issue:

  • i load an image
  • i change that image content locally
  • i want to force glide to update the content/view
  • i try to remove it from caches (disk/cache)
  • but it fails because when i reload the Img view, glide put the current imageDrawable in memory (after i evict it)
  • so when it loads again, he gets it back from memory.

How should i fix that? My prefer way would be to tell glide to NOT put that image into memory cache

Copy link
Contributor Author

Copilot AI commented Dec 23, 2025

@copilot ok i made some changes and almost everything works. Now i have one last issue:

  • i load an image...

Fixed in aac83d0. Added forceReloadImage() method that:

  1. Sets skipMemoryCacheOnNextLoad flag to skip memory cache for next load only
  2. Evicts from all caches (disk + memory)
  3. Clears and reloads the image
  4. Flag automatically resets after use

Usage when image file content changes:

await imageView.forceReloadImage();

This prevents Glide from caching the old drawable in memory during eviction and ensures fresh content is loaded.

Copilot AI changed the title Fix file:// URL handling, cache eviction, and cache detection with efficient disk cache tracking in Android image loader Fix file:// URL handling, cache eviction, cache detection, and force reload with efficient disk cache tracking in Android image loader Dec 23, 2025
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