打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Android图片缓存之Lru算法(二)

Android图片缓存之Lru算法(二)

前言:

     上篇我们总结了Bitmap的处理,同时对比了各种处理的效率以及对内存占用大小。我们得知一个应用如果使用大量图片就会导致OOM(out of memory),那该如何处理才能近可能的降低oom发生的概率呢?之前我们一直在使用SoftReference软引用,SoftReference是一种现在已经不再推荐使用的方式,因为从 Android 2.3 (API Level 9)开始,垃圾回收器会更倾向于回收持有软引用或弱引用的对象,这让软引用变得不再可靠,所以今天我们来认识一种新的缓存处理算法Lru,然后学习一下基于Lru的Lrucache、DiskLruCache 实现我们的图片缓存。

Lru:

   LRU是Least Recently Used 的缩写,翻译过来就是“最近最少使用”,LRU缓存就是使用这种原理实现,简单的说就是缓存一定量的数据,当超过设定的阈值时就把一些过期的数据删除掉,比如我们缓存10000条数据,当数据小于10000时可以随意添加,当超过10000时就需要把新的数据添加进来,同时要把过期数据删除,以确保我们最大缓存10000条,那怎么确定删除哪条过期数据呢,采用LRU算法实现的话就是将最老的数据删掉。

基于LruCache实现内存缓存:

 1.)初始化MemoryCache

  这里内存缓存的是Drawable 而不是Bitmap 理由是Drawable相对Bitmap来说有很大的内存优势

        int maxMemory = (int) Runtime.getRuntime().maxMemory();//获取系统分配给应用的总内存大小        int mCacheSize = maxMemory / 8;//设置图片内存缓存占用八分之一        mMemoryCache = new LruCache<String, Drawable>(mCacheSize) {            //必须重写此方法,来测量Bitmap的大小            @Override            protected int sizeOf(String key, Drawable value) {                if (value instanceof BitmapDrawable) {                    Bitmap bitmap = ((BitmapDrawable) value).getBitmap();                    return bitmap == null ? 0 : bitmap.getByteCount();                }                return super.sizeOf(key, value);            }        };

2.)添加一个Drawable到内存缓存

  /**     * 添加Drawable到内存缓存     *     * @param key     * @param drawable     */    private void addDrawableToMemoryCache(String key, Drawable drawable) {        if (getDrawableFromMemCache(key) == null && drawable != null) {            mMemoryCache.put(key, drawable);        }    }

3.)从内存缓存中获取一个Drawable

    /**     * 从内存缓存中获取一个Drawable     *     * @param key     * @return     */    public Drawable getDrawableFromMemCache(String key) {        return mMemoryCache.get(key);    }

4.)从内存缓存中移除一个Drawable

   /**     * 从内存缓存中移除     *     * @param key     */    public void removeCacheFromMemory(String key) {        mMemoryCache.remove(key);    }

5.)清空内存缓存

    /**     * 清理内存缓存     */    public void cleanMemoryCCache() {        mMemoryCache.evictAll();    }

其实Lru缓存机制本质上就是存储在一个LinkedHashMap存储,为了保障插入的数据顺序,方便清理。

基于DiskLruCache实现磁盘缓存:

   DiskLruCache类并不是谷歌官方实现,需要自行下载,下载地址:https://github.com/JakeWharton/DiskLruCache

  1.)初始化DiskLruCache

       File cacheDir = context.getCacheDir();//指定的是数据的缓存地址        long diskCacheSize = 1024 * 1024 * 30;//最多可以缓存多少字节的数据        int appVersion = DiskLruUtils.getAppVersion(context);//指定当前应用程序的版本号        int valueCount = 1;//指定同一个key可以对应多少个缓存文件        try {            mDiskCache = DiskLruCache.open(cacheDir, appVersion, valueCount, diskCacheSize);        } catch (Exception ex) {        }

2.)写入一个文件到磁盘缓存

    /**     * 添加Bitmap到磁盘缓存     *     * @param key     * @param value     */    private void addBitmapToDiskCache(String key, byte[] value) {        OutputStream out = null;        try {            DiskLruCache.Editor editor = mDiskCache.edit(key);            if (editor != null) {                out = editor.newOutputStream(0);                if (value != null && value.length > 0) {                    out.write(value);                    out.flush();                    editor.commit();                } else {                    editor.abort();                }            }            mDiskCache.flush();        } catch (IOException e) {            e.printStackTrace();        } finally {            DiskLruUtils.closeQuietly(out);        }    }

3.)从磁盘缓存中读取Drawable

    /**     * 从磁盘缓存中获取一个Drawable     *     * @param key     * @return     */    public Drawable getDrawableFromDiskCache(String key) {        try {            DiskLruCache.Snapshot snapShot = mDiskCache.get(key);            if (snapShot != null) {                InputStream is = snapShot.getInputStream(0);                Bitmap bitmap = BitmapFactory.decodeStream(is);                Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);                //从磁盘中读取到之后 加入内存缓存                addDrawableToMemoryCache(key, drawable);                return drawable;            }        } catch (IOException e) {            e.printStackTrace();        }        return null;    }

4.)从磁盘缓存中移除

    /**     * 从磁盘缓存中移除     *     * @param key     */    public void removeCacheFromDisk(String key) {        try {            mDiskCache.remove(key);        } catch (Exception e) {        }    }

5.)清空磁盘缓存

    /**     * 清理磁盘缓存     */    public void cleanDiskCache() {        try {            mDiskCache.delete();        } catch (Exception e) {        }    }

图片下载过程:

   接下来实例中用到了一点RxJava的知识有不了解RxJava的请自行了解一下。

  1.)采用异步方式操作磁盘缓存和网络下载, 内存缓存可以在主线程中操作

   public void disPlay(final ImageView imageView, String imageUrl) {        //生成唯一key        final String key = DiskLruUtils.hashKeyForDisk(imageUrl);        //先从内存中读取        Drawable drawableFromMemCache = getDrawableFromMemCache(key);        if (drawableFromMemCache != null) {            imageView.setImageDrawable(drawableFromMemCache);            return;        }        Observable.just(imageUrl)                .map(new Func1<String, Drawable>() {                    @Override                    public Drawable call(String imageUrl) { // 参数类型 String                        //从磁盘中读取                        Drawable drawableFromDiskCache = getDrawableFromDiskCache(key);                        if (drawableFromDiskCache != null) {                            return drawableFromDiskCache;                        }                        //网络下载                        return download(imageUrl); // 返回类型 Drawable                    }                })                .subscribeOn(Schedulers.io()) // 指定 subscribe() 发生在 IO 线程                .observeOn(AndroidSchedulers.mainThread()) // 指定 Subscriber 的回调发生在主线程                .subscribe(new Action1<Drawable>() {                    @Override                    public void call(Drawable drawable) { // 参数类型 Drawable                        imageView.setImageDrawable(drawable);                    }                });    }

2.)下载图片过程以及处理

 private Drawable download(String imageUrl) {        HttpURLConnection urlConnection = null;        ByteArrayOutputStream bos = null;        InputStream ins = null;        try {            final URL url = new URL(imageUrl);            urlConnection = (HttpURLConnection) url.openConnection();            ins = urlConnection.getInputStream();            bos = new ByteArrayOutputStream();            int b;            while ((b = ins.read()) != -1) {                bos.write(b);            }            bos.flush();            byte[] bytes = bos.toByteArray();            Bitmap bitmap = DiskLruUtils.bytes2Bitmap(bytes);            String key = DiskLruUtils.hashKeyForDisk(imageUrl);            Drawable drawable = DiskLruUtils.bitmap2Drawable(bitmap);            //加入内存缓存            addDrawableToMemoryCache(key, drawable);            //加入磁盘缓存            addBitmapToDiskCache(key, bytes);            return drawable;        } catch (IOException e) {            e.printStackTrace();        } finally {            if (urlConnection != null) {                urlConnection.disconnect();            }            DiskLruUtils.closeQuietly(bos);            DiskLruUtils.closeQuietly(ins);        }        return null;    }

 

附上最终图片缓存单例简单实现全部代码以及DiskLruUtils工具类代码

ImageLoadManager.java
ImageLoadManager.java
DiskLruUtils.java
DiskLruUtils.java

总结:

 以上就是基于Lru图片缓存简单实现


本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
开源项目源码解析
Android使用缓存优化ListView
一起写一个Android图片加载框架
从源代码分析Android
图片加载框架Universal-Image-Loader源码解析
Android 之 远程图片获取和本地缓存
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服