Skip to content

Commit c62d118

Browse files
oprisnikmeta-codesync[bot]
authored andcommitted
Add optional offer-back "second-life" ring to WeakBitmapCountingMemoryCache
Reviewed By: Nehgupta-1203 Differential Revision: D107394433 fbshipit-source-id: 0da014f74e9bc8b799165ca2844784edad679ccc
1 parent ae0f0d6 commit c62d118

5 files changed

Lines changed: 51 additions & 1 deletion

File tree

‎imagepipeline-base/src/main/java/com/facebook/imagepipeline/cache/MemoryCache.kt‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,22 @@ interface MemoryCache<K, V> : MemoryTrimmable, HasDebugData {
4040
*/
4141
fun cache(key: K, value: CloseableReference<V>): CloseableReference<V>?
4242

43+
/**
44+
* Caches a value that is being released by its last UI holder — i.e. Vito's "offer-back" on
45+
* detach (see [com.facebook.imagepipeline.core.ImagePipeline.returnImageToCache]).
46+
*
47+
* Defaults to [cache]. GC-managed caches that hold values only weakly may override this to give
48+
* the released value a bounded strong-reference "second life", so it survives GC long enough to
49+
* be re-served on scroll-back without raising steady-state peak memory.
50+
*
51+
* Wrapping/delegating caches MUST forward this to their delegate(s) rather than inheriting the
52+
* default — otherwise the override on the wrapped cache is never reached.
53+
*
54+
* @return a new reference to be used, or null if the caching failed
55+
*/
56+
fun cacheOnRelease(key: K, value: CloseableReference<V>): CloseableReference<V>? =
57+
cache(key, value)
58+
4359
/**
4460
* Gets the item with the given key, or null if there is no such item.
4561
*

‎imagepipeline/src/main/java/com/facebook/imagepipeline/cache/InstrumentedMemoryCache.java‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,12 @@ public void probe(K key) {
5252
return mDelegate.cache(key, value);
5353
}
5454

55+
@Override
56+
public @Nullable CloseableReference<V> cacheOnRelease(K key, CloseableReference<V> value) {
57+
mTracker.onCachePut(key);
58+
return mDelegate.cacheOnRelease(key, value);
59+
}
60+
5561
@Override
5662
public int removeAll(Predicate<K> predicate) {
5763
return mDelegate.removeAll(predicate);

‎imagepipeline/src/main/java/com/facebook/imagepipeline/cache/RoutingBitmapMemoryCache.kt‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,18 @@ class RoutingBitmapMemoryCache(
5151
}
5252
}
5353

54+
override fun cacheOnRelease(
55+
key: CacheKey,
56+
value: CloseableReference<CloseableImage>,
57+
): CloseableReference<CloseableImage>? {
58+
val image = value.get() ?: return null
59+
return if (image is CloseableBitmap) {
60+
bitmapCache.cacheOnRelease(key, value)
61+
} else {
62+
nonBitmapCache.cacheOnRelease(key, value)
63+
}
64+
}
65+
5466
override fun get(key: CacheKey): CloseableReference<CloseableImage>? =
5567
bitmapCache[key] ?: nonBitmapCache[key]
5668

‎imagepipeline/src/main/java/com/facebook/imagepipeline/core/ImagePipeline.kt‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1071,7 +1071,7 @@ class ImagePipeline(
10711071
val image = imageReference.get() ?: return
10721072
if (image.isStateful) return
10731073
if (!image.qualityInfo.isOfFullQuality) return
1074-
CloseableReference.closeSafely(bitmapMemoryCache.cache(cacheKey, imageReference))
1074+
CloseableReference.closeSafely(bitmapMemoryCache.cacheOnRelease(cacheKey, imageReference))
10751075
}
10761076

10771077
fun hasCachedImage(cacheKey: CacheKey?): Boolean {

‎vito/core-java-impl/src/main/java/com/facebook/fresco/vito/core/impl/FrescoController2Impl.kt‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -490,6 +490,22 @@ open class FrescoController2Impl(
490490
override fun onRelease(drawable: FrescoDrawable2Impl) {
491491
val imageRequest = drawable.imageRequest
492492
if (imageRequest != null) {
493+
// Offer the image back to the memory cache before the drawable closes its reference, so
494+
// weakly held entries (e.g. WeakBitmapCountingMemoryCache) can extend their second life and
495+
// be re-served on scroll-back. Mirrors KFrescoController's offer-back hook.
496+
val useOfferBackBitmap = config.useOfferBackOnRelease()
497+
val useOfferBackNonBitmap = config.useOfferBackOnReleaseForNonBitmapImage()
498+
if (useOfferBackBitmap || useOfferBackNonBitmap) {
499+
val ref = drawable.imageReference
500+
val image = ref?.get()
501+
if (ref != null && CloseableReference.isValid(ref) && image != null) {
502+
val shouldOfferBack =
503+
if (image is CloseableBitmap) useOfferBackBitmap else useOfferBackNonBitmap
504+
if (shouldOfferBack) {
505+
imagePipeline.returnImageToCache(imageRequest, ref)
506+
}
507+
}
508+
}
493509
// Notify listeners
494510
drawable.internalListener.onRelease(
495511
drawable.imageId,

0 commit comments

Comments
 (0)