aboutsummaryrefslogtreecommitdiff
path: root/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java
diff options
context:
space:
mode:
Diffstat (limited to 'library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java')
-rw-r--r--library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java247
1 files changed, 50 insertions, 197 deletions
diff --git a/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java b/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java
index 4682271f..f4fc9be5 100644
--- a/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java
+++ b/library/src/com/bumptech/glide/resize/bitmap_recycle/LruBitmapPool.java
@@ -5,34 +5,52 @@ import static android.content.ComponentCallbacks2.TRIM_MEMORY_MODERATE;
import android.graphics.Bitmap;
import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.Queue;
+import android.os.Build;
public class LruBitmapPool implements BitmapPool {
private static final String TAG = "LruBitmapPool";
- private final GroupedBitmapLinkedMap pool = new GroupedBitmapLinkedMap();
+ private final LruPoolStrategy strategy;
private final int maxSize;
private int currentSize = 0;
+ private int hits;
+ private int misses;
+ private int puts;
+ private int evictions;
+
+ // Exposed for testing only.
+ LruBitmapPool(int maxSize, LruPoolStrategy strategy) {
+ this.maxSize = maxSize;
+ this.strategy = strategy;
+ }
public LruBitmapPool(int maxSize) {
this.maxSize = maxSize;
+ if (Build.VERSION.SDK_INT >= 19) {
+ strategy = new SizeStrategy();
+ } else {
+ strategy = new AttributeStrategy();
+ }
}
@Override
public synchronized boolean put(Bitmap bitmap) {
- final int size = getSize(bitmap);
+ if (!bitmap.isMutable() || strategy.getSize(bitmap) > maxSize) {
+ return false;
+ }
- pool.put(bitmap);
+ final int size = strategy.getSize(bitmap);
+ strategy.put(bitmap);
+ puts++;
currentSize += size;
- evict();
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Put bitmap in pool=" + strategy.logBitmap(bitmap));
+ }
+ dump();
+
+ evict();
return true;
}
@@ -42,14 +60,20 @@ public class LruBitmapPool implements BitmapPool {
@Override
public synchronized Bitmap get(int width, int height, Bitmap.Config config) {
- final Bitmap result = pool.get(width, height, config);
+ final Bitmap result = strategy.get(width, height, config);
if (result == null) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Missing bitmap with dimens=[" + width + "x" + height + "] and config config=" + config);
+ Log.d(TAG, "Missing bitmap=" + strategy.logBitmap(width, height, config));
}
+ misses++;
} else {
- currentSize -= getSize(result);
+ hits++;
+ currentSize -= strategy.getSize(result);
}
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Get bitmap=" + strategy.logBitmap(width, height, config));
+ }
+ dump();
return result;
}
@@ -64,198 +88,27 @@ public class LruBitmapPool implements BitmapPool {
if (level >= TRIM_MEMORY_MODERATE) {
clearMemory();
} else if (level >= TRIM_MEMORY_BACKGROUND) {
- trimToSize(currentSize / 2);
+ trimToSize(maxSize / 2);
}
}
private void trimToSize(int size) {
while (currentSize > size) {
- final Bitmap removed = pool.removeLast();
- currentSize -= getSize(removed);
+ final Bitmap removed = strategy.removeLast();
+ currentSize -= strategy.getSize(removed);
removed.recycle();
- }
- }
-
- private static int getSize(Bitmap bitmap) {
- return bitmap.getHeight() * bitmap.getRowBytes();
- }
-
- /**
- * Similar to {@link java.util.LinkedHashMap} when access ordered except that it is access ordered on groups
- * of bitmaps rather than individual objects. The idea is to be able to find the LRU bitmap size, rather than the
- * LRU bitmap object. We can then remove bitmaps from the least recently used size of bitmap when we need to
- * reduce our cache size.
- *
- * For the purposes of the LRU, we count gets for a particular size of bitmap as an access, even if no bitmaps
- * of that size are present. We do not count addition or removal of bitmaps as an access.
- */
- private static class GroupedBitmapLinkedMap {
- private final Map<Key, LinkedEntry> keyToEntry = new HashMap<Key, LinkedEntry>();
- private final LinkedEntry head = new LinkedEntry();
- private final KeyPool keyPool = new KeyPool();
-
- private static class KeyPool {
- private static final int MAX_SIZE = 20;
-
- private final Queue<Key> keyPool = new LinkedList<Key>();
-
- public Key get(int width, int height, Bitmap.Config config) {
- Key result = keyPool.poll();
- if (result == null) {
- result = new Key();
- }
- result.init(width, height, config);
- return result;
- }
-
- public void offer(Key key) {
- if (keyPool.size() <= MAX_SIZE) {
- keyPool.offer(key);
- }
- }
- }
-
- private static class Key {
- private int width;
- private int height;
- private Bitmap.Config config; //this can be null :(
-
- public void init(int width, int height, Bitmap.Config config) {
- this.width = width;
- this.height = height;
- this.config = config;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Key key = (Key) o;
-
- if (height != key.height) return false;
- if (width != key.width) return false;
- if (config != key.config) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = width;
- result = 31 * result + height;
- result = 31 * result + (config != null ? config.hashCode() : 0);
- return result;
- }
- }
-
- public void put(Bitmap bitmap) {
- final Key key = keyPool.get(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig());
-
- LinkedEntry entry = keyToEntry.get(key);
- if (entry == null) {
- entry = new LinkedEntry(key);
- makeTail(entry);
- keyToEntry.put(key, entry);
- } else {
- keyPool.offer(key);
- }
-
- entry.add(bitmap);
- }
-
- public Bitmap get(int width, int height, Bitmap.Config config) {
- final Key key = keyPool.get(width, height, config);
-
- LinkedEntry entry = keyToEntry.get(key);
- if (entry == null) {
- entry = new LinkedEntry(key);
- keyToEntry.put(key, entry);
- } else {
- keyPool.offer(key);
- }
-
- makeHead(entry);
-
- return entry.removeLast();
- }
-
- public Bitmap removeLast() {
- LinkedEntry last = head.prev;
-
- while (last != head) {
- Bitmap removed = last.removeLast();
- if (removed != null) {
- return removed;
- } else {
- //we will clean up empty lru entries since they are likely to have been one off or unusual sizes
- //and are not likely to be requested again so the gc thrash should be minimal. Doing so will speed
- //up our removeLast operation in the future and prevent our linked list from growing to arbitrarily
- //large sizes
- removeEntry(last);
- keyToEntry.remove(last.key);
- keyPool.offer(last.key);
- }
-
- last = last.prev;
+ evictions++;
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Evicting bitmap=" + strategy.logBitmap(removed));
}
-
- return null;
+ dump();
}
+ }
- private void makeHead(LinkedEntry entry) {
- removeEntry(entry);
- entry.prev = head;
- entry.next = head.next;
- updateEntry(entry);
- }
-
- private void makeTail(LinkedEntry entry) {
- removeEntry(entry);
- entry.prev = head.prev;
- entry.next = head;
- updateEntry(entry);
- }
-
- //after updating entry's next and prev, set
- //those entry's prev and next (respectively) to rentry
- private static void updateEntry(LinkedEntry entry) {
- entry.next.prev = entry;
- entry.prev.next = entry;
- }
-
- private static void removeEntry(LinkedEntry entry) {
- entry.prev.next = entry.next;
- entry.next.prev = entry.prev;
- }
-
- private static class LinkedEntry {
- private List<Bitmap> value;
- private final Key key;
- LinkedEntry next;
- LinkedEntry prev;
-
- //head only
- public LinkedEntry() {
- this(null);
- }
-
- public LinkedEntry(Key key) {
- next = prev = this;
- this.key = key;
- }
-
- public Bitmap removeLast() {
- final int valueSize = value != null ? value.size() : 0;
- return valueSize > 0 ? value.remove(valueSize-1) : null;
- }
-
- public void add(Bitmap bitmap) {
- if (value == null) {
- value = new ArrayList<Bitmap>();
- }
- value.add(bitmap);
- }
+ private void dump() {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Hits=" + hits + " misses=" + misses + " puts=" + puts + " evictions=" + evictions + " currentSize="
+ + currentSize + " maxSize=" + maxSize + "\nStrategy=" + strategy);
}
}
}