android 图片加载框架-内存缓存
来源:互联网 发布:中国招标网软件 编辑:程序博客网 时间:2024/06/06 03:10
概述
缓存可以提高图片加载效率,针对数据源来自网络的图片,还可以减少带宽。缓存一般情况分两类:内存缓存、磁盘缓存。本章主要介绍内存缓存。
怎么来撸一个内存缓存,hashmap?软引用?大小限制?回收规则?一堆的基础需求浮现在了脑海。当看了picasso,universal imageloader ,glide,fresco等图片加载框架,发现内存缓存的实现基本一致,都是使用lrucache。(fresco内存缓存包含两部分:未解码的字节码缓存和bitmap缓存)。
LRUCache实现原理
这个类的代码不长,却实现了上面描述一个内存缓存需要的所有功能。这里面主要归功于lrucache使用了一个具有充当缓存机制的核心引擎,这个类是linkedhashmap。关于linkedhashmap,查看源码可以看到,本质上也是一个hashmap,具有hashmap所有的优点:查询效率高,自动扩充容量。同时,在此基础上增加了指针用来标记存储元素之间的前后关系,每当从map中读取数据的时候,会重新排序,将操作的元素挪动到链表的头部。这样linkedhashmap就具备了最近最少使用的特性。下面来具体看lrucache的代码实现。以picasso源码里的实现作为样本。
LRUCache代码分析
/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package com.squareup.picasso;import android.content.Context;import android.graphics.Bitmap;import android.support.annotation.NonNull;import java.util.Iterator;import java.util.LinkedHashMap;import java.util.Map;import static com.squareup.picasso.Utils.KEY_SEPARATOR;/** A memory cache which uses a least-recently used eviction policy. 内存缓存,最近最少使用清除规则*/public class LruCache implements Cache { //使用LinkedHashMap作为存储器引擎 final LinkedHashMap<String, Bitmap> map; //设定缓存最大值 private final int maxSize; private int size; private int putCount; private int evictionCount; private int hitCount; private int missCount; /** Create a cache using an appropriate portion of the available RAM as the maximum size. */ public LruCache(@NonNull Context context) { //计算默认使用的内存缓存大小,这个大小在不同图片 //加载框架不太一样,picasso默认使用app可分配最大内存 //的15%,具体可以查看Utils类。 this(Utils.calculateMemoryCacheSize(context)); } /** Create a cache with a given maximum size in bytes. */ public LruCache(int maxSize) { if (maxSize <= 0) { throw new IllegalArgumentException("Max size must be positive."); } this.maxSize = maxSize; this.map = new LinkedHashMap<>(0, 0.75f, true); } @Override public Bitmap get(@NonNull String key) { if (key == null) { throw new NullPointerException("key == null"); } Bitmap mapValue; synchronized (this) { mapValue = map.get(key); if (mapValue != null) { hitCount++; return mapValue; } missCount++; } return null; } @Override public void set(@NonNull String key, @NonNull Bitmap bitmap) { if (key == null || bitmap == null) { throw new NullPointerException("key == null || bitmap == null"); } int addedSize = Utils.getBitmapBytes(bitmap); if (addedSize > maxSize) { return; } synchronized (this) { putCount++; size += addedSize; Bitmap previous = map.put(key, bitmap); //查看hashmap源码,previous表示key之前对应的value。 //当key重复,插入新元素以后,会将之前元素替换掉。 //因此此处需要将此前对应value的占用大小减除 if (previous != null) { size -= Utils.getBitmapBytes(previous); } } //每次添加元素,都要重新调整一下缓存的大小。 trimToSize(maxSize); } private void trimToSize(int maxSize) { while (true) { String key; Bitmap value; synchronized (this) { if (size < 0 || (map.isEmpty() && size != 0)) { throw new IllegalStateException( getClass().getName() + ".sizeOf() is reporting inconsistent results!"); } //如果当前缓存使用空间小于最大容量,忽略 if (size <= maxSize || map.isEmpty()) { break; } //从缓存中移除链表尾部的元素,重新计算缓存大小 Map.Entry<String, Bitmap> toEvict = map.entrySet().iterator().next(); key = toEvict.getKey(); value = toEvict.getValue(); map.remove(key); size -= Utils.getBitmapBytes(value); evictionCount++; } } } /** Clear the cache. */ public final void evictAll() { trimToSize(-1); // -1 will evict 0-sized elements } @Override public final synchronized int size() { return size; } @Override public final synchronized int maxSize() { return maxSize; } @Override public final synchronized void clear() { evictAll(); } @Override public final synchronized void clearKeyUri(String uri) { int uriLength = uri.length(); for (Iterator<Map.Entry<String, Bitmap>> i = map.entrySet().iterator(); i.hasNext();) { Map.Entry<String, Bitmap> entry = i.next(); String key = entry.getKey(); Bitmap value = entry.getValue(); int newlineIndex = key.indexOf(KEY_SEPARATOR); if (newlineIndex == uriLength && key.substring(0, newlineIndex).equals(uri)) { i.remove(); size -= Utils.getBitmapBytes(value); } } } /** Returns the number of times {@link #get} returned a value. */ public final synchronized int hitCount() { return hitCount; } /** Returns the number of times {@link #get} returned {@code null}. */ public final synchronized int missCount() { return missCount; } /** Returns the number of times {@link #set(String, Bitmap)} was called. */ public final synchronized int putCount() { return putCount; } /** Returns the number of values that have been evicted. */ public final synchronized int evictionCount() { return evictionCount; }}
总结
Lrucache使用很少的代码量,解决了一个内存缓存需要的诉求:
1. 存储方案,使用map,查找效率高
2. 容量控制,计算app在当前设备所能分配的最大容量,取15%(可调整)
3. 回收策略,最近最少使用,这个策略通过linkedhashmap链表动态调整元素顺序来实现。
这要归功于java语言及其生态环境、开源社区强大的组件库。正如牛顿说,我看的远是因为站在巨人的肩膀上。
思考:
为什么没有使用软引用?
- android 图片加载框架-内存缓存
- Android图片加载缓存框架Glide
- android图片加载框架-磁盘缓存
- 自己写Android图片缓存框架之一级内存缓存
- Android ListView 图片异步加载和图片内存缓存
- Android ListView 图片异步加载和图片内存缓存机制
- Android ListView 图片异步加载和图片内存缓存
- Android ListView 图片异步加载和图片内存缓存
- Android ListView 图片异步加载和图片内存缓存
- Android ListView 图片异步加载和图片内存缓存
- Android图片加载与缓存开源框架:Android Glide
- Android图片加载与缓存开源框架:Android Glide
- Android图片加载与缓存开源框架:Android Glide
- Android图片加载与缓存开源框架:Android Glide
- Android图片加载与缓存开源框架:Android Glide
- Android图片加载与缓存开源框架:Android Glide
- Android图片加载与缓存开源框架:Android Glide
- Android – 加载图片本缓存到内存与本地
- Spring自定义事件
- Python的装饰器
- apue习题12.2实现
- 《机器学习实战》1.K-Means聚类算法
- C4D/MAYA导入Unity填坑以及C4D渲染自我心得
- android 图片加载框架-内存缓存
- 欢迎使用CSDN-markdown编辑器
- springMVC 基于jws的webservice建立
- WebApp开发-CORDOVA踩过的坑
- SRIO switch 调试
- android dialog圆角显示及解决出现的黑色棱角 .
- STM8 串口接收字符串问题
- 搭建git服务器
- hdu1164 Eddy's research I