前言
发现ONE · 一个耗流量有点可怕...便开始学习图片的三级缓存机制(内存缓存、本地缓存、网络缓存),来提高图片的加载速度,同时减少使用App时的流量消耗。
正文
概述
为什么要用图片缓存技术呢
现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。
图片的三级缓存分别是内存、本地、网络缓存。由于从内存拿图片速度最快,本地次之,从网络获取最慢,并且每次都从网路获取比较浪费流量。因此我们要实现的图片三级缓存的图片加载顺序是这样的:
- 从内存获取图片
- 从本地获取图片,并且将图片缓存到内存中
- 从网络获取图片,并且将图片缓存到内存及本地
代码实现
内存缓存
我们在内存缓存中用到了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);
}
});
}