ImageCahe now have counter that keeps reference how many times a bitmap have been requested/shown. (#99)

Bitmap is put in reusable queue only if this bitmap reference counter is 0/null.
This prevents an issue where one big bitmap is added to cache but it exceeds cache limit so it is removed immediately from cache (LRU internals) and marked as reusable.
This commit is contained in:
Hristo Hristov
2017-05-16 17:51:31 +03:00
committed by GitHub
parent cdf16ea47e
commit e887716352
3 changed files with 47 additions and 6 deletions

View File

@@ -34,6 +34,7 @@ import java.lang.ref.SoftReference;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@@ -56,6 +57,7 @@ public class Cache {
private static final boolean DEFAULT_DISK_CACHE_ENABLED = true;
private static Cache instance;
private HashMap<String, Integer> mMemoryCacheUsage;
private LruCache<String, Bitmap> mMemoryCache;
private CacheParams mParams;
@@ -121,6 +123,7 @@ public class Cache {
Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
}
mMemoryCacheUsage = new HashMap<String, Integer>();
mMemoryCache = new LruCache<String, Bitmap>(mParams.memCacheSize) {
/**
@@ -129,7 +132,8 @@ public class Cache {
@Override
protected void entryRemoved(boolean evicted, String key,
Bitmap oldValue, Bitmap newValue) {
if (Utils.hasHoneycomb()) {
Integer count = mMemoryCacheUsage.get(key);
if (Utils.hasHoneycomb() && (count == null || count == 0)) {
// We're running on Honeycomb or later, so add the bitmap
// to a SoftReference set for possible use with inBitmap later
mReusableBitmaps.add(new SoftReference<Bitmap>(oldValue));
@@ -167,6 +171,9 @@ public class Cache {
// because this will make the previous bitmap free for reuse but it is used somewhere.
// Probably won't happen often.
if (currentValue == null) {
Integer count = mMemoryCacheUsage.get(data);
// NOTE: count should be null here.
mMemoryCacheUsage.put(data, count == null ? 1 : count + 1);
mMemoryCache.put(data, value);
}
}
@@ -183,6 +190,10 @@ public class Cache {
if (mMemoryCache != null) {
memValue = mMemoryCache.get(data);
if (memValue != null) {
Integer count = mMemoryCacheUsage.get(data);
mMemoryCacheUsage.put(data, count + 1);
}
}
if (Worker.debuggable > 0 && memValue != null) {
@@ -192,6 +203,19 @@ public class Cache {
return memValue;
}
public void reduceDisplayedCounter(String uri) {
if (mMemoryCache != null) {
Integer count = mMemoryCacheUsage.get(uri);
if (count != null) {
if (count == 1) {
mMemoryCacheUsage.remove(uri);
} else {
mMemoryCacheUsage.put(uri, count - 1);
}
}
}
}
/**
* @param options - BitmapFactory.Options with out* options populated
* @return Bitmap that case be used for inBitmap
@@ -240,6 +264,11 @@ public class Cache {
Log.v(TAG, "Memory cache cleared");
}
}
if (mMemoryCacheUsage != null) {
mMemoryCacheUsage.clear();
}
mMemoryCacheUsage = null;
mMemoryCache = null;
}

View File

@@ -77,6 +77,12 @@ public abstract class Worker {
}
}
public void removeBitmap(String uri) {
if (mCache != null) {
mCache.reduceDisplayedCounter(uri);
}
}
/**
* Load an image specified by the data parameter into an ImageView (override
* {@link Worker#processBitmap(String, int, int, boolean)} to define the processing logic). A memory and
@@ -280,9 +286,8 @@ public abstract class Worker {
*/
@Override
protected Bitmap doInBackground(Void... params) {
final String dataString = String.valueOf(mUri);
if (debuggable > 0) {
Log.v(TAG, "doInBackground - starting work: " + imageViewReference.get() + ", on: " + dataString);
Log.v(TAG, "doInBackground - starting work: " + imageViewReference.get() + ", on: " + mUri);
}
@@ -313,9 +318,9 @@ public abstract class Worker {
if (bitmap != null) {
if (mCache != null && mCacheImage) {
if (debuggable > 0) {
Log.v(TAG, "addBitmapToCache: " + imageViewReference.get() + ", src: " + dataString);
Log.v(TAG, "addBitmapToCache: " + imageViewReference.get() + ", src: " + mUri);
}
mCache.addBitmapToCache(dataString, bitmap);
mCache.addBitmapToCache(mUri, bitmap);
}
}

View File

@@ -169,7 +169,7 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
mAsync = async;
// Clear current bitmap only if we set empty URI.
// We support settimg bitmap through ImageSource (e.g. Bitmap).
// We support setting bitmap through ImageSource (e.g. Bitmap).
if (uri == null || uri.trim() == "") {
this.setImageBitmap(null);
}
@@ -194,6 +194,13 @@ public class ImageView extends android.widget.ImageView implements BitmapOwner {
@Override
public void setImageBitmap(Bitmap bm) {
Fetcher fetcher = Fetcher.getInstance(this.getContext());
// if we have existing bitmap from uri notify fetcher that this bitmap is not shown in this ImageView instance.
// This is needed so that fetcher inner cache could reuse the bitmap only when no other ImageView shows it.
if (mUseCache && mUri != null && mBitmap != null && fetcher != null) {
fetcher.removeBitmap(mUri);
}
super.setImageBitmap(bm);
this.mBitmap = bm;
}