diff --git a/android/widgets/src/main/java/org/nativescript/widgets/Image/Cache.java b/android/widgets/src/main/java/org/nativescript/widgets/Image/Cache.java index 1773d87ab..aa8019967 100644 --- a/android/widgets/src/main/java/org/nativescript/widgets/Image/Cache.java +++ b/android/widgets/src/main/java/org/nativescript/widgets/Image/Cache.java @@ -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 mMemoryCacheUsage; private LruCache mMemoryCache; private CacheParams mParams; @@ -121,6 +123,7 @@ public class Cache { Collections.synchronizedSet(new HashSet>()); } + mMemoryCacheUsage = new HashMap(); mMemoryCache = new LruCache(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(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; } diff --git a/android/widgets/src/main/java/org/nativescript/widgets/Image/Worker.java b/android/widgets/src/main/java/org/nativescript/widgets/Image/Worker.java index d17387746..62bd06056 100644 --- a/android/widgets/src/main/java/org/nativescript/widgets/Image/Worker.java +++ b/android/widgets/src/main/java/org/nativescript/widgets/Image/Worker.java @@ -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); } } diff --git a/android/widgets/src/main/java/org/nativescript/widgets/ImageView.java b/android/widgets/src/main/java/org/nativescript/widgets/ImageView.java index 3a5aeac2a..d76cc8f05 100644 --- a/android/widgets/src/main/java/org/nativescript/widgets/ImageView.java +++ b/android/widgets/src/main/java/org/nativescript/widgets/ImageView.java @@ -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; }