-
Notifications
You must be signed in to change notification settings - Fork 6.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fixes #34
- Loading branch information
Showing
14 changed files
with
891 additions
and
359 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,6 @@ | ||
tests/ant.properties | ||
tests/local.properties | ||
tests/gen/**/* | ||
tests/bin | ||
libs/volley.jar | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
library/src/com/bumptech/glide/resize/bitmap_recycle/AttributeStrategy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package com.bumptech.glide.resize.bitmap_recycle; | ||
|
||
import android.graphics.Bitmap; | ||
|
||
class AttributeStrategy implements LruPoolStrategy { | ||
private final KeyPool keyPool = new KeyPool(); | ||
private final GroupedLinkedMap<Key, Bitmap> groupedMap = new GroupedLinkedMap<Key, Bitmap>(); | ||
|
||
public void put(Bitmap bitmap) { | ||
final Key key = keyPool.get(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); | ||
|
||
groupedMap.put(key, bitmap); | ||
} | ||
|
||
@Override | ||
public Bitmap get(int width, int height, Bitmap.Config config) { | ||
final Key key = keyPool.get(width, height, config); | ||
|
||
return groupedMap.get(key); | ||
} | ||
|
||
@Override | ||
public Bitmap removeLast() { | ||
return groupedMap.removeLast(); | ||
} | ||
|
||
@Override | ||
public String logBitmap(Bitmap bitmap) { | ||
return getBitmapString(bitmap); | ||
} | ||
|
||
@Override | ||
public String logBitmap(int width, int height, Bitmap.Config config) { | ||
return getBitmapString(width, height, config); | ||
} | ||
|
||
@Override | ||
public int getSize(Bitmap bitmap) { | ||
return bitmap.getHeight() * bitmap.getRowBytes(); | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return "AttributeStrategy:\n " + groupedMap; | ||
} | ||
|
||
private static String getBitmapString(Bitmap bitmap) { | ||
return getBitmapString(bitmap.getWidth(), bitmap.getHeight(), bitmap.getConfig()); | ||
} | ||
|
||
private static String getBitmapString(int width, int height, Bitmap.Config config) { | ||
return "[" + width + "x" + height + "], " + config; | ||
} | ||
|
||
private static class KeyPool extends BaseKeyPool<Key> { | ||
public Key get(int width, int height, Bitmap.Config config) { | ||
Key result = get(); | ||
result.init(width, height, config); | ||
return result; | ||
} | ||
|
||
@Override | ||
protected Key create() { | ||
return new Key(this); | ||
} | ||
} | ||
|
||
private static class Key implements Poolable { | ||
private final KeyPool pool; | ||
private int width; | ||
private int height; | ||
// Config can be null :( | ||
private Bitmap.Config config; | ||
|
||
public Key(KeyPool pool) { | ||
this.pool = pool; | ||
} | ||
|
||
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; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
return getBitmapString(width, height, config); | ||
} | ||
|
||
@Override | ||
public void offer() { | ||
pool.offer(this); | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
library/src/com/bumptech/glide/resize/bitmap_recycle/BaseKeyPool.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.bumptech.glide.resize.bitmap_recycle; | ||
|
||
import android.os.Build; | ||
|
||
import java.util.ArrayDeque; | ||
import java.util.LinkedList; | ||
import java.util.Queue; | ||
|
||
abstract class BaseKeyPool<T extends Poolable> { | ||
private static final int MAX_SIZE = 20; | ||
private final Queue<T> keyPool; | ||
|
||
public BaseKeyPool() { | ||
if (Build.VERSION.SDK_INT >= 9) { | ||
keyPool = new ArrayDeque<T>(MAX_SIZE); | ||
} else { | ||
keyPool = new LinkedList<T>(); | ||
} | ||
} | ||
|
||
protected T get() { | ||
T result = keyPool.poll(); | ||
if (result == null) { | ||
result = create(); | ||
} | ||
return result; | ||
} | ||
|
||
public void offer(T key) { | ||
if (keyPool.size() < MAX_SIZE) { | ||
keyPool.offer(key); | ||
} | ||
} | ||
|
||
protected abstract T create(); | ||
} |
146 changes: 146 additions & 0 deletions
146
library/src/com/bumptech/glide/resize/bitmap_recycle/GroupedLinkedMap.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
package com.bumptech.glide.resize.bitmap_recycle; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
|
||
/** | ||
* 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. | ||
*/ | ||
class GroupedLinkedMap<K extends Poolable, V> { | ||
private final LinkedEntry<K, V> head = new LinkedEntry<K, V>(); | ||
private final Map<K, LinkedEntry<K, V>> keyToEntry = new HashMap<K, LinkedEntry<K, V>>(); | ||
|
||
public void put(K key, V value) { | ||
LinkedEntry<K, V> entry = keyToEntry.get(key); | ||
|
||
if (entry == null) { | ||
entry = new LinkedEntry<K, V>(key); | ||
makeTail(entry); | ||
keyToEntry.put(key, entry); | ||
} else { | ||
key.offer(); | ||
} | ||
|
||
entry.add(value); | ||
} | ||
|
||
public V get(K key) { | ||
LinkedEntry<K, V> entry = keyToEntry.get(key); | ||
if (entry == null) { | ||
entry = new LinkedEntry<K, V>(key); | ||
keyToEntry.put(key, entry); | ||
} else { | ||
key.offer(); | ||
} | ||
|
||
makeHead(entry); | ||
|
||
return entry.removeLast(); | ||
} | ||
|
||
public V removeLast() { | ||
LinkedEntry<K, V> last = head.prev; | ||
|
||
while (last != head) { | ||
V 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); | ||
last.key.offer(); | ||
} | ||
|
||
last = last.prev; | ||
} | ||
|
||
return null; | ||
} | ||
|
||
@Override | ||
public String toString() { | ||
String result = "GroupedLinkedMap( "; | ||
LinkedEntry<K, V> current = head.next; | ||
boolean hadAtLeastOneItem = false; | ||
while (current != head) { | ||
hadAtLeastOneItem = true; | ||
result += "{" + current.key + ":" + current.size() + "}, "; | ||
current = current.next; | ||
} | ||
if (hadAtLeastOneItem) { | ||
result = result.substring(0, result.length() - 2); | ||
} | ||
return result + " )"; | ||
} | ||
|
||
// Make the entry the most recently used item. | ||
private void makeHead(LinkedEntry<K, V> entry) { | ||
removeEntry(entry); | ||
entry.prev = head; | ||
entry.next = head.next; | ||
updateEntry(entry); | ||
} | ||
|
||
// Make the entry the least recently used item. | ||
private void makeTail(LinkedEntry<K, V> entry) { | ||
removeEntry(entry); | ||
entry.prev = head.prev; | ||
entry.next = head; | ||
updateEntry(entry); | ||
} | ||
|
||
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<K, V> { | ||
private final K key; | ||
private List<V> values; | ||
LinkedEntry<K, V> next; | ||
LinkedEntry<K, V> prev; | ||
|
||
// Used only for the first item in the list which we will treat specially and which will not contain a value. | ||
public LinkedEntry() { | ||
this(null); | ||
} | ||
|
||
public LinkedEntry(K key) { | ||
next = prev = this; | ||
this.key = key; | ||
} | ||
|
||
public V removeLast() { | ||
final int valueSize = size(); | ||
return valueSize > 0 ? values.remove(valueSize - 1) : null; | ||
} | ||
|
||
public int size() { | ||
return values != null ? values.size() : 0; | ||
} | ||
|
||
public void add(V value) { | ||
if (values == null) { | ||
values = new ArrayList<V>(); | ||
} | ||
values.add(value); | ||
} | ||
} | ||
} |
Oops, something went wrong.
d5ddd93
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@sjudd nicely done!