zoukankan      html  css  js  c++  java
  • Android lrucache 实现与使用(Android内存优化)

    什么是LruCache? 

    LruCache实现原理是什么?

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

    LruCache源码分析

    
    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);
        }
    }
    

    LruCache 使用

    先来看两张内存使用的图

    这里写图片描述

                                 图-1
    

    这里写图片描述

                                图-2
    

    以上内存分析图所分析的是同一个应用的数据,唯一不同的是图-1没有使用 LruCache,而图-2使用了 LruCache;可以非常明显的看到,图-1的内存使用明显偏大,基本上都是在30M左右,而图-2的内存使用情况基本上在20M左右。这就足足省了将近10M的内存!

    ok,下面把实现代码贴出来

    /**
     * Created by gyzhong on 15/4/5.
     */
    public class LruPageAdapter extends PagerAdapter {
    
        private List<String> mData ;
        private LruCache<String,Bitmap> mLruCache ;
        private int mTotalSize = (int) Runtime.getRuntime().totalMemory();
        private ViewPager mViewPager ;
    
        public LruPageAdapter(ViewPager viewPager ,List<String> data){
            mData = data ;
            mViewPager = viewPager ;
            /*实例化LruCache*/
            mLruCache = new LruCache<String,Bitmap>(mTotalSize/5){
    
                /*当缓存大于我们设定的最大值时,会调用这个方法,我们可以用来做内存释放操作*/
                @Override
                protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                    super.entryRemoved(evicted, key, oldValue, newValue);
                    if (evicted && oldValue != null){
                        oldValue.recycle();
                    }
                }
                /*创建 bitmap*/
                @Override
                protected Bitmap create(String key) {
                    final int resId = mViewPager.getResources().getIdentifier(key,"drawable",
                            mViewPager.getContext().getPackageName()) ;
                    return BitmapFactory.decodeResource(mViewPager.getResources(),resId) ;
                }
                /*获取每个 value 的大小*/
                @Override
                protected int sizeOf(String key, Bitmap value) {
                    return value.getByteCount();
                }
            } ;
        }
    
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            View view = LayoutInflater.from(container.getContext()).inflate(R.layout.view_pager_item, null) ;
            ImageView imageView = (ImageView) view.findViewById(R.id.id_view_pager_item);
            Bitmap bitmap = mLruCache.get(mData.get(position));
            imageView.setImageBitmap(bitmap);
            container.addView(view);
            return view;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }
    
        @Override
        public int getCount() {
            return mData.size();
        }
    
    
    
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    }

    总结

    1、LruCache 是基于 Lru算法实现的一种缓存机制; 
    2、Lru算法的原理是把近期最少使用的数据给移除掉,当然前提是当前数据的量大于设定的最大值。 
    3、LruCache 没有真正的释放内存,只是从 Map中移除掉数据,真正释放内存还是要用户手动释放。

     
  • 相关阅读:
    测试模式 windows2008 内部版本7601
    移动端UC /QQ 浏览器的部分私有Meta 属性
    正则表达式 正向预查 负向预查
    获取指定元素的某一个样式属性值
    把普通对象转换成json格式的对象
    求平均数-----类数组转换成数组
    轮播图
    倒计时
    JS 预解释相关理解
    ul ol di三者区别
  • 原文地址:https://www.cnblogs.com/xgjblog/p/9047079.html
Copyright © 2011-2022 走看看