【Android】第三方ONE开发之图片三级缓存的实现

【Android】第三方ONE开发之图片三级缓存的实现

前言

发现ONE · 一个耗流量有点可怕…便开始学习图片的三级缓存机制(内存缓存、本地缓存、网络缓存),来提高图片的加载速度,同时减少使用App时的流量消耗。

正文

概述

为什么要用图片缓存技术呢

现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。

图片的三级缓存分别是内存、本地、网络缓存。由于从内存拿图片速度最快,本地次之,从网络获取最慢,并且每次都从网路获取比较浪费流量。因此我们要实现的图片三级缓存的图片加载顺序是这样的:

  1. 从内存获取图片
  2. 从本地获取图片,并且将图片缓存到内存中
  3. 从网络获取图片,并且将图片缓存到内存及本地

代码实现

内存缓存

我们在内存缓存中用到了LruCache这种容器,它内部是由LinkHashMap来实现的,特点是有内存大小的限制,超过时则把使用最少的资源释放掉存入新资源。这样我们就只用给它设定一个最大的内存限制就不用再操心了。这里我们给它分配的内存大小是1/8的最大内存。然后我们这里定义了两个方法,分别是将图片以url为key存入其中以及通过url取出。这样,内存缓存的部分就成功实现了。

    private static LruCache<String, Bitmap> sMemoryCache;

     public ImageLoader() {
        if (sMemoryCache == null) {
            final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  //将最大内存换算为kb(原来以B为单位)
            final int cacheSize = maxMemory / 8;    //取1/8的内存为LruCache的大小
            //计算LruCache空间大小
            sMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getByteCount() / 1024;
                }
            };
        }
    }

    public void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (getBitmapFromMemoryCache(key) == null) {
            sMemoryCache.put(key, bitmap);  //图片没有放入时将图片放入内存
        }
    }

    public Bitmap getBitmapFromMemoryCache(String key) {
        return sMemoryCache.get(key);   //从内存取出对应图片
    }

本地缓存

我们统一将图片缓存到根目录下的ONE目录下,存放和取出时需要注意的是,这里通过了一次Md5的加密,因为图片的url是不确定的,可能会影响到我们的存储,并且还会暴露我们的接口。所以我们先给它进行Md5加密,再存入。同时要注意的是,我们取出的同时将它存入了内存缓存中,这样就可以加快下一次加载此图片的速度。

    private static final String CACHE_PATH =
            Environment.getExternalStorageDirectory().getAbsolutePath() + "/ONE";   //本地缓存路径
     public Bitmap getBitmapFromLocal(String url) {
        String fileName = null;
        try {
            //进行MD5加密的原因:不让一些特殊的url影响文件的存储
            //同时让接口不被用户看到
            //把图片的url当做文件名,并进行MD5加密
            fileName = MD5Encoder.encode(url);
            File file = new File(CACHE_PATH, fileName);
            Bitmap bitmap = null;
            if (file.exists()) {
                bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
                addBitmapToMemoryCache(url, bitmap); //添加到内存缓存
            }
            return bitmap;
        } catch (Exception e) {
            e.printStackTrace();
        }

        return null;
    }

    public void addBitmapToLocal(String url, Bitmap bitmap) {
        try {
            //进行MD5加密的原因:不让一些特殊的url影响文件的存储
            //同时让接口不被用户看到
            //把图片的url当做文件名,并进行MD5加密
            String fileName = MD5Encoder.encode(url);
            File file = new File(CACHE_PATH, fileName);

            //通过得到文件的父文件,判断父文件是否存在
            File parentFile = file.getParentFile();
            if (!parentFile.exists()) {
                parentFile.mkdirs();
            }

            //把图片保存至本地
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(file));
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

网络缓存

网络缓存就像以前的加载图片一样处理就好,需要注意的是加载图片后还要把图片缓存到本地以及内存中,这样可以加快下次图片加载的速度。

加载图片

加载图片时,我们采用如伪代码一样的方式,这样我们就完成了图片的三级缓存。

    boolean isLoaded = false;
    //内存缓存
    final Bitmap memoryBitmap = getBitmapFromMemoryCache(url);
    if (memoryBitmap != null && !isLoaded) {
        isLoaded = true;
        uiHandler.post(new Runnable() {
            @Override
            public void run() {
                imageView.setImageBitmap(memoryBitmap);
            }
        });
    }
    //本地缓存
    final Bitmap localBitmap = getBitmapFromLocal(url);
    if (localBitmap != null && !isLoaded) {
        isLoaded = true;
        uiHandler.post(new Runnable() {
            @Override
            public void run() {
                imageView.setImageBitmap(localBitmap);
            }
        });
    }
    //网络缓存
    final Bitmap netBitmap = getBitmapFromNet(url);
    if (netBitmap != null && !isLoaded) {
        isLoaded = true;
        uiHandler.post(new Runnable() {
            @Override
            public void run() {
                imageView.setImageBitmap(netBitmap);
            }
        });
    }

发表评论

电子邮件地址不会被公开。 必填项已用*标注

%d 博主赞过: