图片缓存的原理
实现图片缓存也不难,需要有相应的cache策略。这里采用 内存-文件-网络
三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结
构中。

当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图
片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个
url被下载过,其图片就被缓存起来了。

关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空
间足够,垃
圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高
速缓存。使用软引用能防止内存泄露,增强程序的健壮性。

--以上内容选自网络--

1、内存缓存

/**
 * 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。
 * 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。
 */
private static LruCache<String, Bitmap> mLruCache; // 硬引用缓存

private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; // 软引用缓存

private static final int SOFT_CACHE_SIZE = 15;  //软引用缓存容量

主要的构造初始化方法

public ImageLoader(Context context) {
    int memClass = ((ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass();
    int cacheSize = 1024 * 1024 * memClass / 4; // 硬引用缓存容量,为系统可用内存的1/4
    mLruCache = new LruCache<String, Bitmap>(cacheSize) {
        @Override
        protected int sizeOf(String key, Bitmap value) {
            if (value != null)
                return value.getRowBytes() * value.getHeight();
            else
                return 0;
        }

        @Override
        protected void entryRemoved(boolean evicted, String key,
                Bitmap oldValue, Bitmap newValue) {
            if (oldValue != null)
                // 硬引用缓存容量满的时候,会根据LRU算法把最近没有被使用的图片转入此软引用缓存
                mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));
        }
    };
    mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_SIZE, 0.75f, true) {
        private static final long serialVersionUID = 6040103833179403725L;
        @Override
        protected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {
            if (size() > SOFT_CACHE_SIZE){
                return true;
            }
            return false;
        }
    };
}

 ***最主要的获取图片的逻辑方法***

/**
 * 加载获取图片的主要方法
 */
public Bitmap getBitmap(String url) {
    // 从内存缓存中获取图片
    Bitmap result = getBitmapFromCache(url);
    if (result == null) {
        // 文件缓存中获取
        result = getBitmapFromFile(url);
        if (result == null) {
            // 从网络获取
            result = getBitmapFromNet(url);
            if (result != null) {
                saveBitmap(result, url);
                addBitmapToCache(url, result);
            }
        } else {
            // 添加到内存缓存
            addBitmapToCache(url, result);
        }
    }
    return result;
}

从内存缓存中获取图片

/**
 * 从内存缓存中获取图片
 */
public Bitmap getBitmapFromCache(String url) {
    Bitmap bitmap;
    //先从硬引用缓存中获取
    synchronized (mLruCache) {
        bitmap = mLruCache.get(url);
        if (bitmap != null) {
            //如果找到的话,把元素移到LinkedHashMap的最前面,从而保证在LRU算法中是最后被删除
            mLruCache.remove(url);
            mLruCache.put(url, bitmap);
            return bitmap;
        }
    }
    //如果硬引用缓存中找不到,到软引用缓存中找
    synchronized (mSoftCache) {
        SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);
        if (bitmapReference != null) {
            bitmap = bitmapReference.get();
            if (bitmap != null) {
                //将图片移回硬缓存
                mLruCache.put(url, bitmap);
                mSoftCache.remove(url);
                return bitmap;
            } else {
                mSoftCache.remove(url);
            }
        }
    }
    return null;
} 

2、文件缓存

图片文件的保存

/** 将图片存入文件缓存 **/
public void saveBitmap(Bitmap bm, String url) {
    if (bm == null) {
        return;
    }
    // 判断sdcard上的空间
    if (FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
        // SD空间不足
        return;
    }
    String filename = convertUrlToFileName(url);
    String dir = getDirectory();
    File dirFile = new File(dir);
    if (!dirFile.exists())
        dirFile.mkdirs();
    File file = new File(dir + "/" + filename);
    try {
        file.createNewFile();
        OutputStream outStream = new FileOutputStream(file);
        bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);
        outStream.flush();
        outStream.close();
    } catch (FileNotFoundException e) {
    } catch (IOException e) {
    }
}

/** 修改文件的最后修改时间 **/
public void updateFileTime(String path) {
    File file = new File(path);
    long newModifiedTime = System.currentTimeMillis();
    file.setLastModified(newModifiedTime);
}

/** 计算sdcard上的剩余空间 **/
private int freeSpaceOnSd() {
    StatFs stat = new StatFs(Environment.getExternalStorageDirectory()
            .getPath());
    @SuppressWarnings("deprecation")
    double sdFreeMB = ((double) stat.getAvailableBlocks() * (double) stat
            .getBlockSize()) / MB;
    return (int) sdFreeMB;
}

/** 将url转成文件名 **/
private String convertUrlToFileName(String url) {
    String[] strs = url.split("/");
    return strs[strs.length - 1] + WHOLESALE_CONV;
}

/** 获得缓存目录 **/
private String getDirectory() {
    String dir = getSDPath() + "/" + CACHEDIR;
    return dir;
}

/** 取SD卡路径 **/
private String getSDPath() {
    File sdDir = null;
    boolean sdCardExist = Environment.getExternalStorageState().equals(
            android.os.Environment.MEDIA_MOUNTED); // 判断sd卡是否存在
    if (sdCardExist) {
        sdDir = Environment.getExternalStorageDirectory(); // 获取根目录
    }
    if (sdDir != null) {
        return sdDir.toString();
    } else {
        return "";
    }
}

图片文件的获取

/**
 * 从文件缓存中获取图片
 **/
public Bitmap getBitmapFromFile(final String url) {
    final String path = getDirectory() + "/" + convertUrlToFileName(url);
    File file = new File(path);
    if (file.exists()) {
        Bitmap bmp = BitmapFactory.decodeFile(path);
        if (bmp == null) {
            file.delete();
        } else {
            updateFileTime(path);
            return bmp;
        }
    }
    return null;
}

3、网络获取图片

/**
 * 从网络获取图片
 */
public Bitmap getBitmapFromNet(String url) {
    final HttpClient client = new DefaultHttpClient();
    final HttpGet getRequest = new HttpGet(url);

    try {
        HttpResponse response = client.execute(getRequest);
        final int statusCode = response.getStatusLine().getStatusCode();
        if (statusCode != HttpStatus.SC_OK) {
            return null;
        }

        final HttpEntity entity = response.getEntity();
        if (entity != null) {
            InputStream inputStream = null;
            try {
                inputStream = entity.getContent();
                FilterInputStream fit = new FlushedInputStream(inputStream);
                return BitmapFactory.decodeStream(fit);
            } finally {
                if (inputStream != null) {
                    inputStream.close();
                    inputStream = null;
                }
                entity.consumeContent();
            }
        }
    } catch (IOException e) {
        getRequest.abort();
    } catch (IllegalStateException e) {
        getRequest.abort();
    } catch (Exception e) {
        getRequest.abort();
    } finally {
        client.getConnectionManager().shutdown();
    }
    return null;
}

/*
 * An InputStream that skips the exact number of bytes provided, unless it reaches EOF.
 */
static class FlushedInputStream extends FilterInputStream {
    public FlushedInputStream(InputStream inputStream) {
        super(inputStream);
    }

    @Override
    public long skip(long n) throws IOException {
        long totalBytesSkipped = 0L;
        while (totalBytesSkipped < n) {
            long bytesSkipped = in.skip(n - totalBytesSkipped);
            if (bytesSkipped == 0L) {
                int b = read();
                if (b < 0) {
                    break;  // we reached EOF
                } else {
                    bytesSkipped = 1; // we read one byte
                }
            }
            totalBytesSkipped += bytesSkipped;
        }
        return totalBytesSkipped;
    }
}

源码文件http://files.cnblogs.com/files/pear-lemon/ImageLoader.zip

Android 图片三级缓存的更多相关文章

  1. Android 图片三级缓存之内存缓存(告别软引用(SoftRefrerence)和弱引用(WeakReference))

    因为之前项目同事使用了图片三级缓存,今天整理项目的时候发现同事还是使用了软引用(SoftRefrerence)和弱引用(WeakReference),来管理在内存中的缓存.看到这个我就感觉不对了.脑海 ...

  2. 【Android - 进阶】之图片三级缓存的原理及实现

    在Android开发中,如果图片过多,而我们又没有对图片进行有效的缓存,就很容易导致OOM(Out Of Memory)错误.因此,图片的缓存是非常重要的,尤其是对图片非常多的应用.现在很多框架都做了 ...

  3. picasso_强大的Android图片下载缓存库

    tag: android pic skill date: 2016/07/09 title: picasso-强大的Android图片下载缓存库 [本文转载自:泡在网上的日子 参考:http://bl ...

  4. 毕加索的艺术——Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选

    毕加索的艺术--Picasso,一个强大的Android图片下载缓存库,OkHttpUtils的使用,二次封装PicassoUtils实现微信精选 官网: http://square.github.i ...

  5. Android图片二级缓存

    点击下载源代码 想起刚開始写代码的时候,领导叫我写一个头像下载的方法,当时屁颠屁颠就写了一个图片下载的,每次都要去网络上请求,最后直接被pass掉了 当时的思路是这种 后来渐渐地就知道了有二级缓存这东 ...

  6. picasso-强大的Android图片下载缓存库

    编辑推荐:稀土掘金,这是一个针对技术开发者的一个应用,你可以在掘金上获取最新最优质的技术干货,不仅仅是Android知识.前端.后端以至于产品和设计都有涉猎,想成为全栈工程师的朋友不要错过! pica ...

  7. android图片的缓存--节约内存提高程序效率

    如今android应用占内存一个比一个大,android程序的质量亟待提高. 这里简单说说网络图片的缓存,我这边就简单的说说思路 1:网络图片,无疑须要去下载图片,我们不须要每次都去下载. 维护一张表 ...

  8. 如何使用picasso 对Android图片下载缓存

    相比较其他,picasso的图片缓存更加简单一些,他只需要一行代码就可以表述:导入相关jar包 Picasso.with(context).load("图片路径").into(Im ...

  9. Android图片载入缓存框架Glide

    Glide开源框架是Google推荐的图片载入和缓框架,其在Github上的开源地址是:https://github.com/bumptech/glide 当然一个Google推荐的框架肯定就是Vol ...

随机推荐

  1. hdu 3236 二维背包

    明天来一发 hdu 4501  算是这题的简化版吧

  2. 无法打开包括文件:“windows.h”: No such file or directory

      VS2012 出现如下错误: 无法打开包括文件:"windows.h": No such file or directory   解决办法,将 C:\Program Files ...

  3. 高效jQuery的奥秘

    讨论jQuery和javascript性能的文章并不罕见.然而,本文我计划总结一些速度方面的技巧和我本人的一些建议,来提升你的jQuery和javascript代码.好的代码会带来速度的提升.快速渲染 ...

  4. Codeforces Round #369 (Div. 2) C. Coloring Trees DP

    C. Coloring Trees   ZS the Coder and Chris the Baboon has arrived at Udayland! They walked in the pa ...

  5. jquery中append()、prepend()、after()、before()的区别详解

    append() - 在被选元素的结尾插入内容(内容的结尾,比如说有个a标签,则是在</a>这个标签之前添加东西) prepend() - 在被选元素的开头插入内容(内容的开始,比如说有个 ...

  6. FastDFS实现文件上传下载实战

    正好,淘淘商城讲这一块的时候,我又想起来当时老徐让我写过一个关于实现FastDFS实现文件上传下载的使用文档,当时结合我们的ITOO的视频系统和毕业论文系统,整理了一下,有根据网上查到的知识,总结了一 ...

  7. 在CSDN中添加友情连接

    <a bref='http://www......'>友情连接</a><br/> <a bref='http://www......'>友情连接2< ...

  8. 移动终端app测试点总结

    以下所有测试最后必须在真机上完整的执行1.安装.卸载测试 在真机上的以及通过91等第三方的安装与卸载 安装在手机上还是sd卡上 2.启动app测试3.升级测试 数字签名.升级覆盖安装.下载后手动覆盖安 ...

  9. HTML5属性运用

    HTML5 接触移动端,或专注于支持HTML5浏览器进行前端开发的工作者都不会陌生,这个已经普及很广,对于我专注于PC端开发的人来说,觉得陌生但又觉得很熟悉,大家都知道做PC前端开发为了兼容IE老版本 ...

  10. Optimizing Performance: Data Binding(zz)

    Optimizing Performance: Data Binding .NET Framework 4.5 Other Versions   Windows Presentation Founda ...