如果每次加载同一张图片都要从网络获取,那代价实在太大了。所以同一张图片只要从网络获取一次就够了,然后在本地缓存起来,之后加载同一张图片时就从缓存中加载就可以了。从内存缓存读取图片是最快的,但是因为内存容量有限,所以最好再加上文件缓存。文件缓存空间也不是无限大的,容量越大读取效率越低,因此可以设置一个限定大小比如10M,或者限定保存时间比如一天。
因此,加载图片的流程应该是:
1、先从内存缓存中获取,取到则返回,取不到则进行下一步;
2、从文件缓存中获取,取到则返回并更新到内存缓存,取不到则进行下一步;
3、从网络下载图片,并更新到内存缓存和文件缓存。
接下来看内存缓存类:ImageMemoryCache
点击(此处)折叠或打开
-
public class ImageMemoryCache {
-
/**
-
* 从内存读取数据速度是最快的,为了更大限度使用内存,这里使用了两层缓存。
-
* 硬引用缓存不会轻易被回收,用来保存常用数据,不常用的转入软引用缓存。
-
*/
-
private static final int SOFT_CACHE_SIZE = 15; //软引用缓存容量
-
private static LruCache<String, Bitmap> mLruCache; //硬引用缓存
-
private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; //软引用缓存
-
-
public ImageMemoryCache(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 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;
-
}
-
-
/**
-
* 添加图片到缓存
-
*/
-
public void addBitmapToCache(String url, Bitmap bitmap) {
-
if (bitmap != null) {
-
synchronized (mLruCache) {
-
mLruCache.put(url, bitmap);
-
}
-
}
-
}
-
-
public void clearCache() {
-
mSoftCache.clear();
-
}
- }
文件缓存类:ImageFileCache
点击(此处)折叠或打开
-
public class ImageFileCache {
-
private static final String CACHDIR = "ImgCach";
-
private static final String WHOLESALE_CONV = ".cach";
-
-
private static final int MB = 1024*1024;
-
private static final int CACHE_SIZE = 10;
-
private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;
-
-
public ImageFileCache() {
-
//清理文件缓存
-
removeCache(getDirectory());
-
}
-
-
/** 从缓存中获取图片 **/
-
public Bitmap getImage(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;
-
}
-
-
/** 将图片存入文件缓存 **/
-
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) {
-
Log.w("ImageFileCache", "FileNotFoundException");
-
} catch (IOException e) {
-
Log.w("ImageFileCache", "IOException");
-
}
-
}
-
-
/**
-
* 计算存储目录下的文件大小,
-
* 当文件总大小大于规定的CACHE_SIZE或者sdcard剩余空间小于FREE_SD_SPACE_NEEDED_TO_CACHE的规定
-
* 那么删除40%最近没有被使用的文件
-
*/
-
private boolean removeCache(String dirPath) {
-
File dir = new File(dirPath);
-
File[] files = dir.listFiles();
-
if (files == null) {
-
return true;
-
}
-
if (!android.os.Environment.getExternalStorageState().equals(
-
android.os.Environment.MEDIA_MOUNTED)) {
-
return false;
-
}
-
-
int dirSize = 0;
-
for (int i = 0; i < files.length; i++) {
-
if (files[i].getName().contains(WHOLESALE_CONV)) {
-
dirSize += files[i].length();
-
}
-
}
-
-
if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > freeSpaceOnSd()) {
-
int removeFactor = (int) ((0.4 * files.length) + 1);
-
Arrays.sort(files, new FileLastModifSort());
-
for (int i = 0; i < removeFactor; i++) {
-
if (files[i].getName().contains(WHOLESALE_CONV)) {
-
files[i].delete();
-
}
-
}
-
}
-
-
if (freeSpaceOnSd() <= CACHE_SIZE) {
-
return false;
-
}
-
-
return true;
-
}
-
-
/** 修改文件的最后修改时间 **/
-
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());
-
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() + "/" + CACHDIR;
-
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 "";
-
}
-
}
-
-
/**
-
* 根据文件的最后修改时间进行排序
-
*/
-
private class FileLastModifSort implements Comparator<File> {
-
public int compare(File arg0, File arg1) {
-
if (arg0.lastModified() > arg1.lastModified()) {
-
return 1;
-
} else if (arg0.lastModified() == arg1.lastModified()) {
-
return 0;
-
} else {
-
return -1;
-
}
-
}
-
}
-
- }
点击(此处)折叠或打开
-
public class ImageGetFromHttp {
-
private static final String LOG_TAG = "ImageGetFromHttp";
-
-
public static Bitmap downloadBitmap(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) {
-
Log.w(LOG_TAG, "Error " + statusCode + " while retrieving bitmap from " + url);
-
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();
-
Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e);
-
} catch (IllegalStateException e) {
-
getRequest.abort();
-
Log.w(LOG_TAG, "Incorrect URL: " + url);
-
} catch (Exception e) {
-
getRequest.abort();
-
Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e);
-
} 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;
-
}
-
}
- }
点击(此处)折叠或打开
-
/*** 获得一张图片,从三个地方获取,首先是内存缓存,然后是文件缓存,最后从网络获取 ***/
-
public Bitmap getBitmap(String url) {
-
// 从内存缓存中获取图片
-
Bitmap result = memoryCache.getBitmapFromCache(url);
-
if (result == null) {
-
// 文件缓存中获取
-
result = fileCache.getImage(url);
-
if (result == null) {
-
// 从网络获取
-
result = ImageGetFromHttp.downloadBitmap(url);
-
if (result != null) {
-
fileCache.saveBitmap(result, url);
-
memoryCache.addBitmapToCache(url, result);
-
}
-
} else {
-
// 添加到内存缓存
-
memoryCache.addBitmapToCache(url, result);
-
}
-
}
-
return result;
- }