LruCache和DiskLruCache的使用

    LruCache是内存缓存,DiskLruCache对应的磁盘缓存。在学习Bitmap缓存优化的时候学习了这两种缓存方式,我觉得这种策略可以应用于Android的开发中(现在应该都是这样应用的吧),它不仅仅是Bitmap,它可以是商品,也可以是一组数据。现在就以Bitmap为案例,把我知道的关于Bitmap缓存的知识都记录下来。

  LruCache的使用

  LruCache在androidx.collection包下有这样一个类。先说说它的基本使用,首先创建LruCache的实例并分配给它内存的大小,然后重写sizeof方法返回每个Bitmap所占的内存。

// 应用可分配的最大内存数(这里转换为k字节)
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // 分配内存1/8给Bitmap缓存
        int cacheSize = maxMemory / 8;
        mBitmapLruCache = new LruCache<String, Bitmap>(cacheSize) {
            @Override
            protected int sizeOf(@NonNull String key, @NonNull Bitmap bitmap) {
                // 每个Bitmap所占的内存数
                return bitmap.getByteCount() / 1024;
            }
        };

   然后当我们去加载Bitmap的时候,首先去LruCache中查找,如果有就直接从内存中取出,如果没有就从网络下载下来进行加载,并把Bitmap存进LruCache中。LruCache大概的策略就是这样。LruCache里面维护了一个LinkedHashMap,它是一个链表,我们可以把它理解成队列。这个LinkedHashMap初始化的时候使用访问顺序作为队列排列的规则,也就是说这个队列是根据访问的顺序从队头到队尾一次排列的。当我们的图片足够多,队列不足以存储我们的图片的时候,队列就会淘汰掉最近最少访问的Bitmap,也就是队尾的Bitmap。

         

           

    LruCache的源码解析

    ok,上面简单的说明LruCache策略和简单使用,下面我们看看它代码是怎么实现的。

    在看源码之前,这里有必要补充下,首先我们看到LruCache类注释,它说android.util.LruCache包下有个LruCache类,它曾经运行在api12之前的。这个包下LruCache我看了下与androidx.collection包下LruCache代码几乎都相同,不同的是,当队列缓存不足的时候它淘汰的是队列头部的元素,经我查阅其他博客,发现在Java某个版本之前LinkedHashMap插入元素采用的尾插法,而现在的是头插法,就是说它的排列顺序和现在的是相反的,自然淘汰的位置也是相反的。

    好的,我们继续回到LruCache源码解析。

    我们现在LruCache的构造方法,它使用maxSize保存内存缓存的大小, 然后初始化LinkedHashMap,注意到LinkedHashMap的第三个参数,如果设置为false,则队列的排列顺序是按照插入顺序排序的,true则访问顺序排序。

          

    接着,看看它的get方法

 @Nullable
    public final V get(@NonNull 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;
        }
    }

    我们传入一个key去取对应的Bitmap,如果存在,则将对应的Bitmap返回并将它移动到队首;如果不存在,则去create一个Bitmap,这个方法默认返回null,如果我们需要去创建一个Bitmap,可以去重写这个方法。在同步块中,将这个新建的Bitmap放入缓存队列中,这里有两种情况的处理:第一中情况,这个key对应的值之前的存在的,它最重新设置进队列中,这个应该是个处理冲突的一个措施。然后调用entryRemoved方法,这个方法也是默认是个空方法。第二种情况,这个key对应的值之前是null,那么它就会调用safeSizeOf方法返回的值加到队列总的缓存中。safeSizeOf方法又调用了sizeOf方法,sizeOf方法默认返回1。

    我们可以这样理解这个sizeOf方法,当我们没有重写sizeOf方法去返回Bitmap占用内存大小,那么我们设置的内存大小maxSize默认是队列可以缓存最大Bitmap的个数;如果像上面那样设置,那么maxSize是队列的可以缓存的最大内存。

         

    接着看trimToSize方法

          

    这方法主要是去判断队列中缓存的Bitmap是超过了我们设定的缓存的大小,如果超过了我们设定的缓存的大小,则需要移除队列中最近最少访问的条目。size保存的是队列中所有元素的总的内存,maxSize是我们分配的内存。

    我们再看看put方法

        

    这个方法里面的调用的方法上面都介绍过了,主要的逻辑在同步块里,入队设置的元素,并添加元素所占的内存,如果上一个key对应的值不为空,size减去这个值的size。

   

    DiskLruCache的使用

    DiskLruCache磁盘缓存,相对于内存缓存来说它是更慢的,在实际应用中,DiskLruCache通常作为二级缓存。具体的流程应该是这个样子。我们在加载图片的时候,首先去内存缓存(LruCache)中找,没有找到就去磁盘缓存(DiskLruCache)中找,没有找到最后去网络去下载然后解码显示图片,并且将图片保存到磁盘缓存和内存缓存中。

 private void loadImage(ImageView imageView, Beauty item) {
        // 从LruCache中寻找
        Bitmap bitmapFromCache = ImageLoaderUtils.share().getBitmapFromCache(item.getBeautyImg());
        if (bitmapFromCache != null) {
            // 显示图片
            imageView.setImageBitmap(bitmapFromCache);
        } else {
            imageView.setImageResource(R.mipmap.ic_launcher);
            // 内存缓存中没有,开启一个子线程寻找
            TaskScheduler.execute(new Task<Bitmap>() {
                @Override
                public Bitmap doInBackground() {
                    synchronized (mDiskLock) {
                        // 从DiskLruCache寻找
                        Bitmap bitmapFromDiskCache = DiskLruCacheUtils.share().getBitmapFromDiskCache(item.getBeautyImg());
                        if (bitmapFromDiskCache == null) {
                            Log.d(TAG, "bitmapFromDiskCache == null ");
                            // 从网络中下载
                            bitmapFromDiskCache = decodeURL(item.getBeautyImg());
                        }
                        return bitmapFromDiskCache;
                    }
                }

                @Override
                public void onSuccess(Bitmap bitmap) {
                    if (bitmap != null) {
                        // 获取图片成功并显示图片
                        ImageLoaderUtils.share().addBitmapToCache(bitmap, item.getBeautyImg());
                        imageView.setImageBitmap(bitmap);
                    }
                }

            });
        }
    }

 

    

    

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值