图片缓存的原理
实现图片缓存也不难,需要有相应的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. QQ互联登录 微博登录问题

    qq 需要用开放平台的扣扣测试 审核通过后 开放所有用户 微博 出现获取token  个人信息失败  需要在微博里添加测试账号  审核通过后 开放所有用户

  2. RMI的概念

    RMI(Remote Method Invocation)远程方法调用是一种计算机之间利用远程对象互相调用实现双方通讯的一种通讯机制.使用这种机制,某一台计算机上的对象可以调用另外一台计算机上的对象来 ...

  3. zookeeper源码分析(一) 工作原理

    来自:http://www.codedump.info/?p=207 阅读zookeeper代码一段时间(注:是很长一段时间,断断续续得有半年了吧?)之后,我要开始将一些积累下来的东西写下来了,鉴于我 ...

  4. 【File】递归删除文件夹中子级文件/夹,并删除文件夹

    今天有这样一个需求,需要删除某一个文件夹,但是文件夹中还有子级的文件 或者还可能会有文件夹在里面,所以就需要使用一个简单的递归才能将文件夹删除成功,包括文件夹中的子级文件/夹.!!! 其实很简单,就一 ...

  5. Oil Deposits(dfs)

    Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission( ...

  6. wcf,socket,数据传输方式

    WCF的最终目标是通过进程或不同的系统.通过本地网络或是通过Internet收发客户和服务之间的消息. WCF合并了Web服务..net Remoting.消息队列和Enterprise Servic ...

  7. Hi,我还没死(屎)

    HDNOIP没考好,紧接着NOIP又到了,加紧练习:-)

  8. 非传统题【A002】

    [A002]非传统题[难度A]————————————————————————————————————————————————————————————————————————————————————— ...

  9. CentOS7安装docker出错(Transaction check error)

    1. 出错内容: Transaction check error: :-.el7_2..x86_64 conflicts with :-.el7.x86_64 :-.el7_2..x86_64 con ...

  10. ol新属性

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...