• linkedu视频
  • 平面设计
  • 电脑入门
  • 操作系统
  • 办公应用
  • 电脑硬件
  • 动画设计
  • 3D设计
  • 网页设计
  • CAD设计
  • 影音处理
  • 数据库
  • 程序设计
  • 认证考试
  • 信息管理
  • 信息安全
菜单
linkedu.com
  • 网页制作
  • 数据库
  • 程序设计
  • 操作系统
  • CMS教程
  • 游戏攻略
  • 脚本语言
  • 平面设计
  • 软件教程
  • 网络安全
  • 电脑知识
  • 服务器
  • 视频教程
  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号
您的位置:首页 > 程序设计 >Android > Android 缓存类LruCache源码分析

Android 缓存类LruCache源码分析

作者:网友 字体:[增加 减小] 来源:互联网 时间:2017-05-26

网友通过本文主要向大家介绍了android lrucache,lrucache缓存,lrucache源码,lrucache,lrucache原理等相关知识,希望对您有所帮助,也希望大家支持linkedu.com www.linkedu.com

Android 缓存类LruCache源码分析


1.引言

Android开发难免会遇到加载网络图片问题,而加载网络图片难免会遇到多次重复请求加载同一张图片问题,这么一来就导致多次加载网络,不仅浪费资源而且给用户体验感觉加载图片慢。从Android3.1开始,API中多了一个LruCache类,该类是一个使用最近最少使用算法的缓存类。也就是可以把上次下载的图片以键值对的方式保存在缓存中,以便下载加载同一张图片时无须再次从网络上下载,而且加载速度快。

2.LruCache源码详解

这里我把LruCache源码贴出来,几乎每行代码的注释都有,很详细,有意者可以仔细跟着注释阅读源码,以便理解LruCache类的原理。

/**
1.从类名LruCache就知道,该类的作用是一个最近最少使用算法来维护的一个缓存类。
2.该类是一个用于缓存一定数量的值,并且该缓存对象是持有强引用。
3.每当成功get一次值时,该值都会移动到链表的头部,以便标记该值为最近最新的值。
4.当缓存满了时,链表尾部的值会被认为是最近最少使用的值,会被从链表中移除,以便缓存有空间保存新的值。
5.缓存中链表的值发生改变时会调用空方法entryRemoved,开发者可以重写该方法,以便做相应的操作。
6.开发者应该去重写该类中sizeOf方法,该方法返回每个key对应value值得大小,以便该类去维护缓存的大小。
7.该类是一个安全类。同时该类不允许key和value为空。该类在Android3.1之后添加到源码中。
**/
package android.util;

import java.util.LinkedHashMap;
import java.util.Map;


public class LruCache {
    private final LinkedHashMap map;

    /** Size of this cache in units. Not necessarily the number of elements. */
    private int size;//已使用缓存大小
    private int maxSize;//总的缓存大小

    private int putCount;//添加记录次数
    private int createCount;//创建次数
    private int evictionCount;//移除次数
    private int hitCount;//命中次数
    private int missCount;//未命中次数

    /**
     * @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(0, 0.75f, true);//创建链表缓存,该链表缓存是整个LruCache类的重点。
    }

    /**
     * Sets the size of the cache.
     * @param maxSize The new maximum size.
     *重新设置缓存大小
     * @hide
     */
    public void resize(int maxSize) {
        if (maxSize <= 0) {
            throw new IllegalArgumentException("maxSize <= 0");
        }

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

    /**
     * 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.
     * 根据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++;//标记未命中的次数
        }

        /*
         * 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);//创建新的数据,默认返回null,创建的时候可以重写该方法,以便未命中的时候创建一个新的数据添加到缓存链表中。
        if (createdValue == null) {
            return null;//未命中时默认到这里结束。
        }

        //以下代码是在未命中时创建新数据添加到链表缓存中。
        synchronized (this) {
            createCount++;//标记创建新数据的次数
            mapValue = map.put(key, createdValue);
            //如果链表中已有该key对应的值,则最后取消添加新创建的值。很多人可能感到奇怪,此时链表中应该不存在key对应的值啊?其实这里是为了防止多线程操作导致数据不同步而添加的安全代码。不懂得自己体会去吧!
            if (mapValue != null) {
                // There was a conflict so undo that last put
                map.put(key, mapValue);
            } else {
                //标记已经使用了的内存大小。
                size += safeSizeOf(key, createdValue);
            }
        }

        if (mapValue != null) {
            //链表中发生数据变化时(调用了put,get方法)调用该方法。该方法默认是个空,开发者可以重写该方法在链表中数据变化时做相应的操作。
            entryRemoved(false, key, createdValue, mapValue);
            return mapValue;
        } else {
            //重新整理当前链表维护的内存。
            trimToSize(maxSize);
            return createdValue;
        }
    }

    /**
     * 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) {//链表中已存在key对应的值则替换原来的值
                size -= safeSizeOf(key, previous);//标记已使用的内存大小
            }
        }

        if (previous != null) {
            //链表中数据发生交换时调用该方法。
            entryRemoved(false, key, previous, value);
        }
        //整理链表维护的内存大小
        trimToSize(maxSize);
        return previous;
    }

    /**
     * @param maxSize the maximum size of the cache before returning. May be -1
     *     to evict even 0-sized elements.
     * 管理链表中内存的大小
     */
    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;
                }

                // BEGIN LAYOUTLIB CHANGE
                // get the last item in the linked list.
                // This is not efficient, the goal here is to minimize the changes
                // compared to the platform version.
                Map.Entry toEvict = null;
                //for循环得到链表末尾的数据,然后移除它。
                for (Map.Entry entry : map.entrySet()) {
                    toEvict = entry;
                }
                // END LAYOUTLIB CHANGE

                if (toEvict == null) {
                    break;
                }
                //移除链表末尾的一个数据,该数据就是最近最少使用到的数据。
                key = toEvict.getKey();
                value = toEvict.getValue();
                map.remove(key);//移除数据
                size -= safeSizeOf(key, value);//重写计算已使用的内存大小
                evictionCount++;//标记移除数据的次数
            }
            //开发者可以重新改方法,当移除数据时做相应的操作。
 



 
分享到:QQ空间新浪微博腾讯微博微信百度贴吧QQ好友复制网址打印

您可能想查找下面的文章:

  • LruCache原理解析,lrucache解析
  • Android 缓存类LruCache源码分析

相关文章

  • 2017-05-26arcgis andriod 加载影像,arcgisandriod
  • 2017-05-26我的android学习经历5,android学习经历5
  • 2017-05-26如何让光标处于EditText的末尾,光标edittext末尾
  • 2017-05-26Android热补丁动态修复技术(三)—— 使用Javassist注入字节码,完成热补丁框架雏形(可使用)
  • 2017-05-26Git版本控制工具(一)----git的安装及创建版本库,git----git
  • 2017-05-26Android开发:内存机制分析——堆和栈
  • 2017-05-225.2.4 Fragment实例精讲——底部导航栏+ViewPager滑动切换页面
  • 2017-05-26Android内存泄漏排查利器LeakCanary,androidleakcanary
  • 2017-05-26android6.0的坑,android6.0
  • 2017-05-26Apache Cordova开发Android应用程序——番外篇,cordovaandroid

文章分类

  • JavaScript
  • ASP.NET
  • PHP
  • 正则表达式
  • AJAX
  • JSP
  • ASP
  • Flex
  • XML
  • 编程技巧
  • Android
  • swift
  • C#教程
  • vb
  • vb.net
  • C语言
  • Java
  • Delphi
  • 易语言
  • vc/mfc
  • 嵌入式开发
  • 游戏开发
  • ios
  • 编程问答
  • 汇编语言
  • 微信小程序
  • 数据结构
  • OpenGL
  • 架构设计
  • qt
  • 微信公众号

最近更新的内容

    • 集成websocket即时通讯 java聊天源码 代码下载 java后台框架源码 websocket源码 IM,websocket即时通讯
    • [android] 手机卫士自定义对话框布局,android卫士
    • android:giavity和layout_gravity的区别
    • 硅谷社交3--登录页面,硅谷社交3--
    • Android程序版本更新--通知栏更新下载安装,android程序版本
    • dpdk内存管理——内存初始化
    • 美女的秘密项目源码,美女秘密项目源码
    • Android与H5互调(通过实例来了解Hybrid App),androidhybrid
    • Android自定义控件(二),android自定义控件
    • Android Intent调用 Uri的使用几种格式,androidintent

关于我们 - 联系我们 - 免责声明 - 网站地图

©2020-2025 All Rights Reserved. linkedu.com 版权所有