在Android中常用的缓存策略是LruCache和DiskLruCache
其中LruCache常被用作内存缓存。Lru是Least Recently Used的缩写
核心思想是:当缓存快满的时候,会移除较早使用的缓存对象,然后添加新的缓存对象
我们平常在项目中如果需要用到缓存,一般都是这么使用的
private TextView text2;
private ImageView imageView;
private LruCache<String,Bitmap> mCaChe;
private Handler handler = new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_touch_dispatch);
initCaChe();
initView();
}
private void initView(){
text2 = findViewById(R.id.text);
imageView = findViewById(R.id.image);
String str = "https://goss.veer.com/creative/vcg/veer/800water/veer-170484153.jpg";
text2.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Bitmap bitmap = getCacheImage("bitmap");
if (bitmap == null){
loadImage();
}else {
imageView.setImageBitmap(bitmap);
}
}
});
}
private void initCaChe(){
long maxMemory = Runtime.getRuntime().maxMemory()/1024;
int cacheSize = (int) (maxMemory / 8);
mCaChe = new LruCache<String,Bitmap>(cacheSize){
//重写sizeof方法,计算出要缓存的每张图片的大小
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight()/1024;
}
};
}
/**
* 读取缓存中Bitmap
* @param key
* @return
*/
private Bitmap getCacheImage(String key){
Bitmap bitmap = mCaChe.get(key);
return bitmap;
}
private void removeCache(String key){
mCaChe.remove(key);
}
/**
* 添加缓存
* @param key
* @param bitmap
*/
private void addCache(String key, Bitmap bitmap){
if (getCacheImage(key) == null){
mCaChe.put(key,bitmap);
}
}
private void loadImage(){
String str = "http://5b0988e595225.cdn.sohucs.com/images/20171210/362dcd1c009842ff99b33f5d51bbfb80.jpeg";
OkHttpClient okHttpClient = new OkHttpClient();
Request request = new Request.Builder().url(str).get().build();
Call call = okHttpClient.newCall(request);
call.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
}
@Override
public void onResponse(Call call, Response response) throws IOException {
final byte[] picture = response.body().bytes();
handler.post(new Runnable() {
@Override
public void run() {
Bitmap bitmap = BitmapFactory.decodeByteArray(picture,0,picture.length);
addCache("bitmap",bitmap);
imageView.setImageBitmap(bitmap);
}
});
}
});
}
我们来看一下LruCache的源码:
/**
* @param maxSize for caches that do not override {@link #sizeOf}, this is
* the maximum number of entries in the cache. For all other caches,
* this is the maximum sum of the sizes of the entries in this cache.
*/
public LruCache(int maxSize) {
if (maxSize <= 0) {
throw new IllegalArgumentException("maxSize <= 0");
}
this.maxSize = maxSize;
this.map = new LinkedHashMap<K, V>(0, 0.75f, true);
}
由构造方法可以看出,LruCache内部采用的是LinkedHashMap
添加数据的put方法
/**
* Caches {@code value} for {@code key}. The value is moved to the head of
* the queue.
*
* @return the previous value mapped by {@code key}.
*/
public final V put(K key, V value) {
if (key == null || value == null) {
throw new NullPointerException("key == null || value == null");
}
V previous;
synchronized (this) {
putCount++;
size += safeSizeOf(key, value);
previous = map.put(key, value);
if (previous != null) {
size -= safeSizeOf(key, previous);
}
}
if (previous != null) {
entryRemoved(false, key, previous, value);
}
trimToSize(maxSize);
return previous;
}
这段代码就是把key/value键值对,放入map中,并扩充每次图片的大小,size增加,如果map中存在这个key,则size减去之前图片的大小
获取数据的get方法
/**
* Returns the value for {@code key} if it exists in the cache or can be
* created by {@code #create}. If a value was returned, it is moved to the
* head of the queue. This returns null if a value is not cached and cannot
* be created.
*/
public final V get(K key) {
if (key == null) {
throw new NullPointerException("key == null");
}
V mapValue;
synchronized (this) {
mapValue = map.get(key);
if (mapValue != null) {
hitCount++;
return mapValue;
}
missCount++;
}
/*
* Attempt to create a value. This may take a long time, and the map
* may be different when create() returns. If a conflicting value was
* added to the map while create() was working, we leave that value in
* the map and release the created value.
*/
V createdValue = create(key);
if (createdValue == null) {
return null;
}
synchronized (this) {
createCount++;
mapValue = map.put(key, createdValue);
if (mapValue != null) {
// There was a conflict so undo that last put
map.put(key, mapValue);
} else {
size += safeSizeOf(key, createdValue);
}
}
if (mapValue != null) {
entryRemoved(false, key, createdValue, mapValue);
return mapValue;
} else {
trimToSize(maxSize);
return createdValue;
}
}
当调用LruCache的get()方法获取集合中的缓存对象时,就代表访问了一次该元素,将会更新队列,保持整个队列是按照访问顺序排序,这个更新过程就是在LinkedHashMap中的get()方法中完成的。
综上所述
LruCache主要是内置了LinkedHash。它的最近最少使用原则也是通过LinkedHashMap来实现的。
我们接下来看一下LinkedHashMap这个类
LinkedHashMap是HashMap的子类,但是内部还有一个双向链表维护键值对的顺序,每个键值对即位于哈希表中,也位于双向链表中,LinkedHashMap支持两种顺序
插入顺序 访问顺序
- 插入顺序:先添加的在前面,后添加的在后面,修改操作不影响顺序
*访问顺序:对一个健执行get/put操作后,其对应的键值会移到链表末尾
我们在代码中测试一下:
public class TestLinkedHashMap {
public static void main(String[] args){
//插入顺序
Map<String,String> map = new LinkedHashMap<>();
map.put("1","abc");
map.put("2","cde");
map.put("3","dddd");
map.put("4","44444");
map.get("2");
map.put("2","2222");
System.out.println(map.toString());
//访问顺序
Map<String,String> linkedHashMap = new LinkedHashMap(16,0.75f,true);
linkedHashMap.put("1","abc");
linkedHashMap.put("2","cde");
linkedHashMap.put("3","dddd");
linkedHashMap.put("4","44444");
linkedHashMap.get("2");
linkedHashMap.put("2","2222");
System.out.println(linkedHashMap.toString());
}
}
运行结果如下:

可以看到在访问顺序的请款下:最近访问修改的,移到了末尾
网友评论