Android LruBitmapPool详情
1. 初始化
// Exposed for testing only. 私有构造方法
LruBitmapPool(int maxSize, LruPoolStrategy strategy, Set<Bitmap.Config> allowedConfigs) {
this.initialMaxSize = maxSize; // 初始值最大size
this.maxSize = maxSize; // 最大size
this.strategy = strategy; // 策略
this.allowedConfigs = allowedConfigs; // 允许bitmap.config集合
this.tracker = new NullBitmapTracker(); // 实例化一个空的bitmap追踪器 空实现
}
// 传入LRU缓存可接受的最大值
public LruBitmapPool(int maxSize) {
this(maxSize,
getDefaultStrategy(), // 获取默认策略
getDefaultAllowedConfigs()); // 获取默认Bitmap.Config
}
// 传入LRU缓存可接受的最大值 并设置Bitmap.Config的配置参数信息 默认是ARGB_8888占4个字节
public LruBitmapPool(int maxSize, Set<Bitmap.Config> allowedConfigs) {
this(maxSize, getDefaultStrategy(), allowedConfigs);
}
// 设置默认的策略
private static LruPoolStrategy getDefaultStrategy() {
final LruPoolStrategy strategy; // 设置为LruPoolStrategy 最近最少使用策略
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // >= 19
// bitmap size 和 bitmap.config一起作为key保存信息
strategy = new SizeConfigStrategy();
} else {
// 一种重用位图的策略,要求返回的位图的尺寸与这些请求完全匹配。 width height bitmap.config 作为key
strategy = new AttributeStrategy();
}
return strategy;
}
private static Set<Bitmap.Config> getDefaultAllowedConfigs() {
Set<Bitmap.Config> configs = new HashSet<Bitmap.Config>();
configs.addAll(Arrays.asList(Bitmap.Config.values()));
if (Build.VERSION.SDK_INT >= 19) { // SDK版本大于19 添加一个null结尾
configs.add(null);
}
// 这个方法的作用主要是将集合转为不可更改的集合 里面使用final修饰并返回当前集合
return Collections.unmodifiableSet(configs);
}
Bitmap.Config.values()
public static enum Config { // 所有数据信息
ALPHA_8,
RGB_565,
ARGB_4444,
ARGB_8888,
RGBA_F16,
HARDWARE;
private Config() {
}
}
2. 使用
使用put向缓存池内添加bitmap数据
public synchronized boolean put(Bitmap bitmap) {
if (bitmap == null) { // bitmap不能为null
throw new NullPointerException("Bitmap must not be null");
} else if (bitmap.isMutable() && this.strategy.getSize(bitmap) <= this.maxSize && this.allowedConfigs.contains(bitmap.getConfig())) {
// bitmap.isMutable() 可复用
// this.strategy.getSize(bitmap) <= this.maxSize bitmap大小小于等于最大size
// this.allowedConfigs.contains(bitmap.getConfig()) bitmap.config在集合中有
int size = this.strategy.getSize(bitmap); // 获取当前bitmap的size大小
this.strategy.put(bitmap); // 将bitmap保存到策略中
this.tracker.add(bitmap); // 将bitmap保存到追踪器中 因为使用的是NullBitmapTracker空实现 不做处理
++this.puts; // 添加数据 自增
this.currentSize += size; // 当前使用size
if (Log.isLoggable("LruBitmapPool", 2)) {
Log.v("LruBitmapPool", "Put bitmap in pool=" + this.strategy.logBitmap(bitmap));
}
this.dump(); // 打印相关log
this.evict(); // 开始判断当前占用空间大小与总大小的处理
return true; // 添加成功返回true
} else {
if (Log.isLoggable("LruBitmapPool", 2)) {
Log.v("LruBitmapPool", "Reject bitmap from pool, bitmap: " + this.strategy.logBitmap(bitmap) + ", is mutable: " + bitmap.isMutable() + ", is allowed config: " + this.allowedConfigs.contains(bitmap.getConfig()));
}
return false;
}
}
this.strategy.put(bitmap); SizeConfigStrategy . put(Bitmap bitmap)
public void put(Bitmap bitmap) {
int size = Util.getBitmapByteSize(bitmap); // 获取bitmap的size
// 将bitmap的size 和 bitmap.config作为参数创建一个key
SizeConfigStrategy.Key key = this.keyPool.get(size, bitmap.getConfig());
this.groupedMap.put(key, bitmap); // 将key和对应的bitmap添加到对应的链表map集合中
// 获取当前bitmap.config类型对应的treeMap集合
NavigableMap<Integer, Integer> sizes = this.getSizesForConfig(bitmap.getConfig());
Integer current = (Integer)sizes.get(key.size); // 根据bitmap的大小获取value
sizes.put(key.size, current == null ? 1 : current + 1); //更新集合内的数据 如果为null? 1 : current+1
}
static class KeyPool extends BaseKeyPool<SizeConfigStrategy.Key> {
KeyPool() { }
public SizeConfigStrategy.Key get(int size, Config config) {
SizeConfigStrategy.Key result = (SizeConfigStrategy.Key)this.get(); // 获取一个实例对象key
result.init(size, config); // 对传入的size和config进行初始化
return result;
}
protected SizeConfigStrategy.Key create() { // 创建一个key实例对象
return new SizeConfigStrategy.Key(this);
}
}
private NavigableMap<Integer, Integer> getSizesForConfig(Config config) {
// 获取集合中是否有符合bitmap.config的数据信息
NavigableMap<Integer, Integer> sizes = (NavigableMap)this.sortedSizes.get(config);
if (sizes == null) { // 没有相关数据
sizes = new TreeMap(); // 实例化一个树map
this.sortedSizes.put(config, sizes); // 将bitmap.config 和 treeMap添加到HashMap集合中
}
return (NavigableMap)sizes; // 返回当前集合信息
}
BaseKeyPool
abstract class BaseKeyPool<T extends Poolable> {
private static final int MAX_SIZE = 20;
private final Queue<T> keyPool = Util.createQueue(20);
BaseKeyPool() { }
protected T get() {
T result = (Poolable)this.keyPool.poll(); // 移除并返问队列头部的元素 如果队列为空,则返回null
if (result == null) { // 如果队列为空 即创建一个实例对象
result = this.create(); // 调用到KeyPool.create()方法
}
return result; // 返回当前
}
public void offer(T key) {
if (this.keyPool.size() < 20) {
this.keyPool.offer(key); // 添加一个元素并返回true,如果队列已满,则返回false
}
}
protected abstract T create();
}
Queue相关方法
add 增加一个元索,如果队列已满,则抛出一个IIIegaISlabEepeplian异常
remove 移除并返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
element 返回队列头部的元素,如果队列为空,则抛出一个NoSuchElementException异常
offer 添加一个元素并返回true,如果队列已满,则返回false
poll 移除并返问队列头部的元素,如果队列为空,则返回null
peek 返回队列头部的元素,如果队列为空,则返回null
put 添加一个元素,如果队列满,则阻塞
take 移除并返回队列头部的元素,如果队列为空,则阻塞
this.groupedMap.put(key, bitmap); 将key和对应的bitmap添加到集合中
GroupedLinkedMap . class
public void put(K key, V value) { // K Key V Bitmap
// 根据key获取到缓存集合中对应的Entry实例类信息
GroupedLinkedMap.LinkedEntry<K, V> entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key);
if (entry == null) { // 如果集合中没有
entry = new GroupedLinkedMap.LinkedEntry(key); // 新建一个Entry
this.makeTail(entry); // 添加到尾部
this.keyToEntry.put(key, entry); // 在将entry和对应的key添加到keyToEntry集合中
} else {
key.offer(); // 添加一个元素并返回true,如果队列已满,则返回false
}
entry.add(value); // 将value添加到entry中
}
private void makeTail(GroupedLinkedMap.LinkedEntry<K, V> entry) {
removeEntry(entry); // 将entry从当前链表中进行移除
entry.prev = this.head.prev; // 将entry的前一个指向head的前一个对象
entry.next = this.head; // 将entry的下一个指向head
updateEntry(entry); // 更新entry
}
// 双向链表 移除当前的entry对象 从链表是移除
private static <K, V> void removeEntry(GroupedLinkedMap.LinkedEntry<K, V> entry) {
entry.prev.next = entry.next; // 将当前entry的前一个指向下一个对象改为指向entry的下一个
entry.next.prev = entry.prev; // 将当前entry的下一个指向前一个对象改为指向entry的上一个
}
private static <K, V> void updateEntry(GroupedLinkedMap.LinkedEntry<K, V> entry) {
entry.next.prev = entry; // 将entry的下一个的prev指向entry
entry.prev.next = entry; // 将entry的上一个的next执行entry
}
下面是操作图示
removeEntry
makeTail
updateEntry
this.dump() 打印相关信息
private void dump() {
if (Log.isLoggable("LruBitmapPool", 2)) {
this.dumpUnchecked();
}
}
private void dumpUnchecked() {
Log.v("LruBitmapPool", "Hits=" + this.hits + ", misses=" + this.misses + ", puts=" + this.puts + ", evictions=" + this.evictions + ", currentSize=" + this.currentSize + ", maxSize=" + this.maxSize + "\nStrategy=" + this.strategy);
}
this.evict();
private void evict() {
this.trimToSize(this.maxSize);
}
private synchronized void trimToSize(int size) { // 传入最大的size
for(; this.currentSize > size; this.dump()) { // 如果currentSize > size 需要做处理
Bitmap removed = this.strategy.removeLast(); // 移除最近最少使用的数据
if (removed == null) { // 如果没有 表示已经没有数据了 将currentSize设置为0 并返回
if (Log.isLoggable("LruBitmapPool", 5)) {
Log.w("LruBitmapPool", "Size mismatch, resetting");
this.dumpUnchecked();
}
this.currentSize = 0;
return;
}
this.tracker.remove(removed); // 将追踪器中的对象进行移除
this.currentSize -= this.strategy.getSize(removed); // currentSize进行重新设置
removed.recycle(); // bitmap对象回收处理
++this.evictions; // 操作+1
if (Log.isLoggable("LruBitmapPool", 3)) {
Log.d("LruBitmapPool", "Evicting bitmap=" + this.strategy.logBitmap(removed));
}
}
}
this.strategy.removeLast(); SizeConfigStrategy . removeLast()
public Bitmap removeLast() {
Bitmap removed = (Bitmap)this.groupedMap.removeLast(); // 获取当前需要移除的bitmap对象
if (removed != null) {
int removedSize = Util.getBitmapByteSize(removed); // 获取当前bitmap的size
this.decrementBitmapOfSize(removedSize, removed.getConfig()); // 减少位图size
}
return removed;
}
private void decrementBitmapOfSize(Integer size, Config config) {
NavigableMap<Integer, Integer> sizes = this.getSizesForConfig(config); // 根据config获取到treeMap集合
Integer current = (Integer)sizes.get(size); // 根据bitmap的size获取当前使用数量
if (current == 1) { // 是有一次
sizes.remove(size); // 直接进行remove处理
} else {
sizes.put(size, current - 1); // 更新集合 并将使用次数进行-1操作
}
}
(Bitmap)this.groupedMap.removeLast(); GroupedLinkedMap . removeLast()
public V removeLast() {
// last.equals(this.head) 数据头与数据头的前一个数据不等 表示有数据 否则表示没有数据直接返回null
for(GroupedLinkedMap.LinkedEntry last = this.head.prev; !last.equals(this.head); last = last.prev) {
V removed = last.removeLast(); // 从集合中进行移除并返回移除元素的对象
if (removed != null) { // 对象不为空
return removed; // 直接将对象返回
}
removeEntry(last); // 直接移除当前entry
this.keyToEntry.remove(last.key); // keyToEntry中移除掉对应的信息
((Poolable)last.key).offer(); // 添加一个元素并返回true,如果队列已满,则返回false
}
return null;
}
((Poolable)last.key).offer(); 调用到 SizeConfigStrategy.Key.offer()
public void offer() {
this.pool.offer(this); // 将当前的key对象添加到pool队列中
}
BaseKeyPool.offer()
public void offer(T key) {
if (this.keyPool.size() < 20) { // 缓存池中小于最大数
this.keyPool.offer(key); // 添加一个元素
}
}
GroupedLinkedMap.LinkedEntry.removeLast() V bitmap K SizeConfigStrategy.Key
public V removeLast() {
int valueSize = this.size(); // 获取当前values集合的size
// 如果为0 则返回null 不为0 则移除最后一个元素 并返回当前移除元素的Value对象
return valueSize > 0 ? this.values.remove(valueSize - 1) : null;
}
public int size() {
return this.values != null ? this.values.size() : 0; // 集合不为null 返回size 为空 返回0
}
3.获取
使用中获取对应的bitmap
public synchronized Bitmap get(int width, int height, Config config) {
// 获取到当前目标bitmap 并将当前bitmap从集合中移除
Bitmap result = this.getDirty(width, height, config);
if (result != null) { // 如果不为null
result.eraseColor(0);
}
return result; // 返回得到的bitmap
}
@TargetApi(12)
public synchronized Bitmap getDirty(int width, int height, Config config) {
// 根据 宽高config 来拿到一个Bitmap对象
Bitmap result = this.strategy.get(width, height, config != null ? config : DEFAULT_CONFIG);
if (result == null) { // 如果为空
if (Log.isLoggable("LruBitmapPool", 3)) {
Log.d("LruBitmapPool", "Missing bitmap=" + this.strategy.logBitmap(width, height, config));
}
++this.misses; // 自增
} else {
++this.hits; // 自增
this.currentSize -= this.strategy.getSize(result); // 获取bitmap的size 并减去
this.tracker.remove(result); // 追踪器集合中移除
if (VERSION.SDK_INT >= 12) { // SDK 大于等于 12
result.setHasAlpha(true); // 是否设置Alpha true
}
}
if (Log.isLoggable("LruBitmapPool", 2)) {
Log.v("LruBitmapPool", "Get bitmap=" + this.strategy.logBitmap(width, height, config));
}
this.dump(); // 打印相关信息
return result;
}
this.strategy.get(width, height, config != null ? config : DEFAULT_CONFIG);
public Bitmap get(int width, int height, Config config) {
int size = Util.getBitmapByteSize(width, height, config); // 根据宽高config类型 获取bitmap的size
SizeConfigStrategy.Key targetKey = this.keyPool.get(size, config); // 根据size和config获取到key对象
SizeConfigStrategy.Key bestKey = this.findBestKey(targetKey, size, config); // 获取最好的key
Bitmap result = (Bitmap)this.groupedMap.get(bestKey); // 根据key获取到GroupMap中对应的bitmap 并移除当前集合
if (result != null) { // bitmap不为null
// 根据config 移除/current-1 sortedSizes分类集合处理
this.decrementBitmapOfSize(Util.getBitmapByteSize(result), result.getConfig());
// 对bitmap进行重新配置 config为null 设置默认为ARGB_8888
result.reconfigure(width, height, result.getConfig() != null ? result.getConfig() : Config.ARGB_8888);
}
return result;
}
private SizeConfigStrategy.Key findBestKey(SizeConfigStrategy.Key key, int size, Config config) {
SizeConfigStrategy.Key result = key;
Config[] arr$ = getInConfigs(config); // 根据对应的config获取到对应的config集合
int len$ = arr$.length; // 获取config集合的长度
for(int i$ = 0; i$ < len$; ++i$) {
Config possibleConfig = arr$[i$];
// 获取config对应的集合 TreeMap
NavigableMap<Integer, Integer> sizesForPossibleConfig = this.getSizesForConfig(possibleConfig);
// 1. 如果有对应相等的size,就返回相等的key对应的size
// 2. 如果有对应比root小的,就返回比目标size稍微大一些的var值
// 3. 如果有对应比root大的,并且一直比跟所有的根节点大,返回root。 如果没有比所有根节点大就返回稍微大一点的key对应的size
Integer possibleSize = (Integer)sizesForPossibleConfig.ceilingKey(size);
if (possibleSize != null && possibleSize <= size * 8) { // 如果root为null 就返回null && 获取size <= size * 8
if (possibleSize == size) { // possibleSize == size 好像只有possibleSize=0 size=0 成立
if (possibleConfig == null) { // config为null
if (config == null) { // 传入config为null
break; // 退出循环
}
} else if (possibleConfig.equals(config)) { // config相等
break; // 退出循环
}
}
this.keyPool.offer(key); // 向缓存池中添加key对象
result = this.keyPool.get(possibleSize, possibleConfig); // 获取当前size的一个key对象 使用缓存池发方式节省内存
break; // 退出循环
}
}
return result;
}
private static Config[] getInConfigs(Config requested) {
switch(requested) {
case ARGB_8888:
return ARGB_8888_IN_CONFIGS;
case RGB_565:
return RGB_565_IN_CONFIGS;
case ARGB_4444:
return ARGB_4444_IN_CONFIGS;
case ALPHA_8:
return ALPHA_8_IN_CONFIGS;
default:
return new Config[]{requested};
}
}
(Bitmap)this.groupedMap.get(bestKey);// 根据key获取到对应的bitmap对象
public V get(K key) {
// 获取key对应的链表entry
GroupedLinkedMap.LinkedEntry<K, V> entry = (GroupedLinkedMap.LinkedEntry)this.keyToEntry.get(key);
if (entry == null) { // 如果为空
entry = new GroupedLinkedMap.LinkedEntry(key); // 实例化一个链表entry对象
this.keyToEntry.put(key, entry); // 将key和entry添加到集合
} else {
key.offer(); // 添加一个元素
}
this.makeHead(entry); // entry添加到链表头
return entry.removeLast(); // 移除操作 并返回移除的bitmap对象
}
TreeMap.ceilingKey()
public K ceilingKey(K var1) {
return keyOrNull(this.getCeilingEntry(var1));
}
static <K, V> K keyOrNull(TreeMap.Entry<K, V> var0) {
return var0 == null ? null : var0.key;
}
final TreeMap.Entry<K, V> getCeilingEntry(K var1) {
TreeMap.Entry var2 = this.root; // 获取到根节点
while(var2 != null) { // 如果点前节点不为null
int var3 = this.compare(var1, var2.key); // 将根节点的key 与 传入进来的key进行对比
if (var3 < 0) { // 表示var1 比 var2.key 小 向左边进行遍历
if (var2.left == null) { // 左边节点为null
return var2; // 直接返回当前var值
}
var2 = var2.left; // 继续向左边延伸
} else { // >= 0 的情况 表示 var1 大于等于 var2.key
if (var3 <= 0) { // 相等的情况
return var2; // 直接返回当前var
}
if (var2.right == null) { // 右节点为null
TreeMap.Entry var4 = var2.parent; // 获取到var2的父节点
// 进行遍历获取到第一个连续又节点的var值
for(TreeMap.Entry var5 = var2; var4 != null && var5 == var4.right; var4 = var4.parent) {
var5 = var4;
}
return var4;
}
var2 = var2.right; // 继续向右延伸
}
}
return null; // 返回空
}
红色的是树节点分发的方向,蓝色是假设当前解析走向。最终是以right结尾。然后查询到绿色位置并返回当前位置var对象。图有点草率……
result.eraseColor(0);
public void eraseColor(@ColorInt int c) {
checkRecycled("Can't erase a recycled bitmap"); // 检查是否被回收 回收则抛出异常
if (!isMutable()) { // 是否可复用
throw new IllegalStateException("cannot erase immutable bitmaps");
}
nativeErase(mNativePtr, c); // native 擦除处理
}
4. 总结
- 构建LRU缓存池时,有两种策略: AttributeStrategy(width,height,config)作为key;SizeConfigStrategy(size,config)作为key进行存储信息。
- 如果未进行设置Bitmap.Config,默认是ARGB_8888一个像素会占用4个byte,会消耗内存较多。如果可以进行设置一下为RGB_565一个像素占用2个byte。内存节约了一半。
- 添加bitmap时,会一直判断currentSize是否大于maxSize,只要开始大于,就开始移除掉最近最少使用的bitmap元素,然后将移除掉的元素添加到keyPool中,以供下次put的时候获取资源使用,以此来节省内存空间。
- 获取对应的bitmap时,会将之前存放在集合中的数据进行移除操作,包括追踪器和groupedMap集合中。