Android 图片三级缓存

图片缓存的原理
实现图片缓存也不难,需要有相应的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)