文档章节

android的内存优化-------LruCache

gavin_jin
 gavin_jin
发布于 2016/11/10 00:48
字数 960
阅读 27
收藏 3
点赞 0
评论 0

概念:

LruCache 
什么是LruCache? 
LruCache实现原理是什么?

这两个问题其实可以作为一个问题来回答,知道了什么是 LruCache,就只然而然的知道 LruCache 的实现原理;Lru的全称是Least Recently Used ,近期最少使用的!所以我们可以推断出 LruCache 的实现原理:把近期最少使用的数据从缓存中移除,保留使用最频繁的数据,那具体代码要怎么实现呢,我们进入到源码中看看。

LruCache源码分析

package android.support.v4.util;

import java.util.LinkedHashMap;
import java.util.Map;
public class LruCache<K, V> {
    //缓存 map 集合,为什么要用LinkedHashMap
    //因为没错取了缓存值之后,都要进行排序,以确保
    //下次移除的是最少使用的值
    private final LinkedHashMap<K, V> map;
    //当前缓存的值
    private int size;
    //最大值
    private int maxSize;
    //添加到缓存中的个数
    private int putCount;
    //创建的个数
    private int createCount;
    //被移除的个数
    private int evictionCount;
    //命中个数
    private int hitCount;
    //丢失个数
    private int missCount;

    //实例化 Lru,需要传入缓存的最大值
    //这个最大值可以是个数,比如对象的个数,也可以是内存的大小
    //比如,最大内存只能缓存5兆
    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);
    }

    //重置最大缓存的值
    public void resize(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }

        synchronized (this) {
            this.maxSize = maxSize;
        }
        trimToSize(maxSize);
    }

    //通过 key 获取缓存值
    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++;
        }


        //如果没有,用户可以去创建
        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;
        }
    }

    //添加缓存,跟上面这个方法的 create 之后的代码一样的
    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;
    }

    //检测缓存是否越界
    private void trimToSize(int maxSize) {
        while (true) {
            K key;
            V value;
            synchronized (this) {
                if (size < 0 || (map.isEmpty() && size != 0)) {
                    throw new IllegalStateException(getClass().getName()
                            + ".sizeOf() is reporting inconsistent results!");
                }
                //如果没有,则返回
                if (size <= maxSize) {
                    break;
                }
                //以下代码表示已经超出了最大范围
                Map.Entry<K, V> toEvict = null;
                for (Map.Entry<K, V> entry : map.entrySet()) {
                    toEvict = entry;
                }

                if (toEvict == null) {
                    break;
                }
                //移除最后一个,也就是最少使用的缓存
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);
                size -= safeSizeOf(key, value);
                evictionCount++;
            }

            entryRemoved(true, key, value, null);
        }
    }

    //手动移除,用户调用
    public final V remove(K key) {
        if (key == null) {
            throw new NullPointerException("key == null");
        }

        V previous;
        synchronized (this) {
            previous = map.remove(key);
            if (previous != null) {
                size -= safeSizeOf(key, previous);
            }
        }

        if (previous != null) {
            entryRemoved(false, key, previous, null);
        }

        return previous;
    }
    //这里用户可以重写它,实现数据和内存回收操作
    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}


    protected V create(K key) {
        return null;
    }

    private int safeSizeOf(K key, V value) {
        int result = sizeOf(key, value);
        if (result < 0) {
            throw new IllegalStateException("Negative size: " + key + "=" + value);
        }
        return result;
    }

        //这个方法要特别注意,跟我们实例化 LruCache 的 maxSize 要呼应,怎么做到呼应呢,比如 maxSize 的大小为缓存的个数,这里就是 return 1就 ok,如果是内存的大小,如果5M,这个就不能是个数 了,这是应该是每个缓存 value 的 size 大小,如果是 Bitmap,这应该是 bitmap.getByteCount();
    protected int sizeOf(K key, V value) {
        return 1;
    }

    //清空缓存
    public final void evictAll() {
        trimToSize(-1); // -1 will evict 0-sized elements
    }


    public synchronized final int size() {
        return size;
    }


    public synchronized final int maxSize() {
        return maxSize;
    }


    public synchronized final int hitCount() {
        return hitCount;
    }


    public synchronized final int missCount() {
        return missCount;
    }


    public synchronized final int createCount() {
        return createCount;
    }


    public synchronized final int putCount() {
        return putCount;
    }


    public synchronized final int evictionCount() {
        return evictionCount;
    }


    public synchronized final Map<K, V> snapshot() {
        return new LinkedHashMap<K, V>(map);
    }
}
}

© 著作权归作者所有

共有 人打赏支持
gavin_jin
粉丝 26
博文 109
码字总数 115950
作品 0
海淀
技术主管
Android内存优化之内存缓存

什么是缓存? 缓存技术原理就是把用户访问的所有对象看作一个全集,经过算法标记哪些是用户经常访问的对象,把这些对象放到一个集合里,这个集合是全集一个子集,下一次用户再访问的时候会先...

今晚打猴子
2015/08/21
0
0
安卓图片的异步请求及使用LruCache缓存和手机内存两层存储图片,避免重新加载页面带来的重新请求

看到网友的一片技术博客讲解了LruCache的使用,我把它加到了我的项目中,但是加入断点发现,列表上下滑动时,确实可以不用重新加载图片,但是重新打开这个activity或者重新启动应用,LruCach...

bluecoffee
2015/04/03
0
0
【转】Android照片墙应用实现,再多的图片也不怕崩溃

出处:http://blog.csdn.net/guolinblog/article/details/9526203 照片墙这种功能现在应该算是挺常见了,在很多应用中你都可以经常看到照片墙的身影。它的设计思路其实也非常简单,用一个Gri...

bluecoffee
2015/04/02
0
0
Android照片墙应用实现,再多的图片也不怕崩溃

转载请注明出处:http://blog.csdn.net/guolinblog/article/details/9526203 照片墙这种功能现在应该算是挺常见了,在很多应用中你都可以经常看到照片墙的身影。它的设计思路其实也非常简单,...

嘻哈开发者
2014/04/17
0
0
Android照片墙应用实现,再多的图片也不怕崩溃

转载请注明出处:http://blog.csdn.net/guolinblog/article/details/9526203 照片墙这种功能现在应该算是挺常见了,在很多应用中你都可以经常看到照片墙的身影。它的设计思路其实也非常简单,...

mingxun
2014/04/16
0
0
Android源码解析——LruCache

我认为在写涉及到数据结构或算法的实现类的源码解析博客时,不应该急于讲它的使用或马上展开对源码的解析,而是要先交待一下这个数据结构或算法的资料,了解它的设计,再从它的设计出发去讲如...

浩码农
2016/05/13
0
0
Android LruCache 缓存图片

使用图片缓存技术 在 你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来。在很多情况下,(比如使用 ListView, GridView 或...

娶到笨笨
2014/04/23
0
0
【Google官方教程】第三课:缓存Bitmap

转载声明:Ryan的博客文章欢迎您的转载,但在转载的同时,请注明文章的来源出处,不胜感激! :-) http://my.oschina.net/ryanhoo/blog/88443 译者:Ryan Hoo 来源:https://developer.andro...

RyanHoo
2012/11/11
0
18
Android使用 LruCache 缓存图片

使用图片缓存技术 在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来。在很多情况下,(比如使用ListView, GridView 或者...

wanyang
2014/01/07
0
0
android如何释放图片缓存

在你应用程序的UI界面加载一张图片是一件很简单的事情,但是当你需要在界面上加载一大堆图片的时候,情况就变得复杂起来。在很多情况下,(比如使用ListView, GridView 或者 ViewPager 这样的...

7潜伏7
2014/04/17
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

崛起于Springboot2.X之thymeleaf(24)

1、依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency> 2、application.properties spring......

木九天
8分钟前
0
0
spring-boot:run启动时,指定spring.profiles.active

Maven启动指定Profile通过-P,如mvn spring-boot:run -Ptest,但这是Maven的Profile。 如果要指定spring-boot的spring.profiles.active,则必须使用mvn spring-boot:run -Drun.profiles=test......

夜黑人模糊灬
10分钟前
0
0
大数据分析挖掘技术学习:Python文本分类

引言 文本分类作为自然语言处理任务之一,被广泛应用于解决各种商业领域的问题。文本分类的目的是将 文本/文档 自动地归类为一种或多种预定义的类别。常见的文本分类应用如下: • 理解社交媒...

加米谷大数据
15分钟前
0
0
istio-0.8 指标监控,prometheus,grafana

配置: https://istio.io/docs/tasks/telemetry/metrics-logs/ https://istio.io/docs/tasks/telemetry/tcp-metrics/ envoy拦截请求>上报mixer>对接prometheus>grafana 效果截图: promethe......

xiaomin0322
16分钟前
0
0
公众号推荐

阿里技术 书籍:《不止代码》

courtzjl
19分钟前
0
0
关于改进工作效率

1.给不同的业务线建立需求群,所有的数据需求都在群里面提。 2.对于特别难搞定的事情,到对应的技术哪去做,有问题随时沟通。 3.定期给工作总结形成方法论。 4.学习新的技术,尝试用新的方法...

Avner
26分钟前
0
0
关于thinkphp 框架开启路径重写,无法获取Authorization Header

今天遇到在thinkphp框架中获取不到header头里边的 Authorization ,后来在.htaccess里面加多一项解决,记录下: <IfModule mod_rewrite.c> Options +FollowSymlinks -Multiviews Rewrite......

殘留回憶
30分钟前
0
0
centos 使用yum安装nginx后如何添加模块 10

centos 使用yum安装nginx后如何添加模块 10 centos6.2版本,使用yum来安装了nginx,但是最近需要重新添加模块,所以就傻了,询问下有人知道怎么重新添加模块吗? PS:俺是新手,需要高手救助...

linjin200
33分钟前
0
0
dubbo 资料

dubbo资料网站: https://www.cnblogs.com/a8457013/p/7818925.html

zaolonglei
34分钟前
0
0
大型网站,你是如何架构的?

大型网站,你是如何架构的?

微小宝
36分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部