结合volley实现图片三级缓存

请几天用xutils大致实现了图片加载的本地缓存,后来又看了一下volley框架,volley对图片的处理还是很强大的,有三种方式:imagerequest,imageloader,networkimageview。

imagerequest就是将从服务器读取的数据直接包装成bitmap,通过回调listener获取该对象;

imageloader主要是增加了imagecache,增加了图片的缓存;

networkimageview是一个控件,可以通过imageloader直接通过url地址直接获取图片显示出来,因为用到imageloader了,所以也会应用到imagecache缓存图片。

在应用过程中如果需要直接显示在界面的image,直接使用networkimageview会很方便,但是当imagecache满了之后再想其中增加图片,就会按照时间顺序删除图片直到空余内存足够存入新的图片,那么再次加载刚删除的图片的话就需要再次从网络加载,这就浪费了。

所以再将缓存的图片存入本地,如果imagecache中没有则从本地SD卡加载,就可以节省流量了。使用networkimageview更方便,networkimageview应用imageloader会实现imagecache接口来缓存,但是本地缓存需要自己实现,首先看networkimageview加载图片的方法:

mNetworkImageView.setImageUrl(Imgurl, imageLoader);
可以看到networkimageview是直接通image的url来加载图片的,imageloader实现的imagecache就会用到,imagecache是个接口,需要自己实现:
public class BitmapLruCache extends LruCache implements ImageLoader.ImageCache {

    public static int getDefaultLruCacheSize() {
        // 拿到最大内存
        final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        // 拿到内存的八分之一来做图片内存缓存
        final int cacheSize = maxMemory / 8;
        return cacheSize;
    }

    public BitmapLruCache() {
        this(getDefaultLruCacheSize());
    }

    public BitmapLruCache(int sizeInKiloBytes) {
        super(sizeInKiloBytes);
    }

    @Override
    protected int sizeOf(String key, Bitmap value) {
        return value.getRowBytes() * value.getHeight() / 1024;
    }

    @Override
    public Bitmap getBitmap(String url) {
        return bitmap;
    }

    @Override
    public void putBitmap(String url, final Bitmap bitmap) {
        put(url, bitmap);
    }
}
主要是getBitmap和putBitmap两个方法,networkimageview在调用setimageURL时就会调用getbitmap,其url为key,如果返回值bitmap为空,则从网络加载,并将其通过putbitmap添加进缓存,下次读取就可以从缓存中读取了,但是前提是缓存没有因为满了将其删除掉。


那么如果删除掉怎么办呢?就是加入第2层本地缓存,在putbitmap中加入以下代码:

final File file = new File(Environment.getExternalStorageDirectory() + "/文件夹名/",
                url);
        try {
            file.createNewFile();
            FileOutputStream out = new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.WEBP, 100, out);
            out.flush();
            out.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
当然要先创建文件夹,上边代码没写,此处用url当文件名,但是因为特殊字符会抛错,所以需要MD5一下,上边代码中也没写。这样就可以在图片从网络加载完成后将图片写入本地缓存。

那么在读取时读取方法不变,还是使用networkimageview的setimageURL,需要更改的是实现imagecache接口中的getBitmap方法,可以看到上面中的getBitmap通过url获取bitmap,但是如果没有该值的话就会返回null,这里可以判断一下,

@Override
    public Bitmap getBitmap(String url) {
        Bitmap bitmap = get(url);
        if (bitmap == null) {
            File file = new File(Environment.getExternalStorageDirectory()
                    + "/文件夹/" + url);
            if (file.exists()) {
                try {
                    FileInputStream inputStream = new FileInputStream(
                            Environment.getExternalStorageDirectory() + "/文件夹/" + url);
                    bitmap = BitmapFactory.decodeStream(inputStream);
                    putBitmap(url, bitmap);
                    Log.e("图片读取来源", "本地缓存");
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            } else {
                Log.e("图片读取来源", "网路");
            }
        } else {
            Log.e("图片读取来源", "lru缓存");
        }
        return bitmap;
    }
可以看到上面代码中先从lrucache中通过get方法读取,如果为空,则判断本地文件夹里是否含有该文件,有就从中读取,没有就从网络去读,需要注意的是如果从网络读取的图片会自动加入缓存,而从本地读取的图片需要通过putbitmap方法存入缓存。注意MD5一下。


再次需要注意的是networkimageview会在加载图片的时候根据其大小对图片进行压缩,并将压缩的信息加在图片的url后:

结合volley实现图片三级缓存_第1张图片

如上图所示,可以打印日志看一下保存的名字是哪个url被MD5后的,方便单独拿取,但是因为这里在使用的时候只需要简单的调用networkimageview的setimageURL方法,不需要注意该项。


再注意一点:本地缓存有一个disklrucache可以直接使用,我自己写完后才知道,而且用起来还是没自己写的熟悉,所以就暂时没用。


再注意一点,在我对文件下载的时候会有大量的图片需要下载,从下载开始到保存完毕的过程中内存是这个样子:

结合volley实现图片三级缓存_第2张图片


还报oom,而且界面开始卡顿,划不动。但是这里图片并不大,只是量多而已,volley应该就适合这样的情况啊,后来多次试验发现是bitmap在写入本地时造成的,我的第一反应是使用子线程来往本地写,自然采用线程池,测验后界面也不卡顿了,oom也没了。

你可能感兴趣的:(Android)