本文主要分析以下几点
- 为什么使用三级缓存
- 什么是三级缓存
- 三级缓存原理
- 代码的具体实现:有注释
- 工具类使用方法
- github分享地址
1、为什么使用三级缓存
- 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了
- 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响
- 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知
- 因此提出三级缓存策略,通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪费流量
2、什么是三级缓存
- 网络缓存, 不优先加载, 速度慢,浪费流量
- 本地缓存, 次优先加载, 速度快
- 内存缓存, 优先加载, 速度最快
3、三级缓存原理
- 首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
- 之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
- 总之,只在初次访问新内容时,才通过网络获取图片资源
4、代码的具体实现:有注释
- 图片缓存工具类
/**
* 图片加载工具类(三级缓存)
* <p>
* 注意:
* 1.首次加载 Android App 时,肯定要通过网络交互来获取图片,之后我们可以将图片保存至本地SD卡和内存中
* 2.之后运行 App 时,优先访问内存中的图片缓存,若内存中没有,则加载本地SD卡中的图片
* 3.总之,只在初次访问新内容时,才通过网络获取图片资源
* Created by zxp on 2016/6/17.
*/
public class ImageLoaderUtils {
private static volatile ImageLoaderUtils imageLoaderUtils;
private MenoryCacheUtils menoryCacheUtils;//内存缓存
private LocalCacheUtils localCacheUtils;//本地缓存
private NetBitmapUtils netBitmapUtils;//网络加载
//单例模式
public static ImageLoaderUtils getInstance() {
if (null == imageLoaderUtils) {
synchronized (ImageLoaderUtils.class) {
if (null == imageLoaderUtils) {
imageLoaderUtils = new ImageLoaderUtils();
}
}
}
return imageLoaderUtils;
}
/**
* 加载图片
*
* @param imageView 图片视图
* @param url 图片地址
*/
public void setImageViewFromBitmap(ImageView imageView, String url) {
setImageViewFromBitmap(imageView, url, NetBitmapUtils.BITMAP_INSAMPLESIZE_ONE);
}
/**
* 加载图片
*
* @param imageView 图片视图
* @param url 图片地址
* @param compressNumber 压缩倍率
*/
public void setImageViewFromBitmap(ImageView imageView, String url, int compressNumber) {
if (menoryCacheUtils == null) {
menoryCacheUtils = new MenoryCacheUtils();
}
if (localCacheUtils == null) {
localCacheUtils = new LocalCacheUtils();
}
if (netBitmapUtils == null) {
netBitmapUtils = new NetBitmapUtils(menoryCacheUtils, localCacheUtils);
}
Bitmap bitmap;
//从内存缓存
bitmap = menoryCacheUtils.getBitmap(url);
if (bitmap != null) {
setImageView(imageView, bitmap);
return;
}
//本地缓存
bitmap = localCacheUtils.getBitmap(url);
if (bitmap != null) {
setImageView(imageView, bitmap);
menoryCacheUtils.saveBitmap(url, bitmap);
return;
}
//网络获取
netBitmapUtils.getBitmap(imageView, url, compressNumber);
}
//设置图片
private void setImageView(ImageView imageView, Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
}
}
- 内存缓存工具类
/**
* 内存缓存
* Created by zxp on 2016/6/17.
*/
public class MenoryCacheUtils {
private LruCache<String, Bitmap> lruCache;
public MenoryCacheUtils() {
//通常得到手机最大内存的1/8,如果app在使用过程中超过这个,会自动回收
long maxMenory = Runtime.getRuntime().maxMemory() / ;
lruCache = new LruCache<String, Bitmap>((int) maxMenory) {
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getByteCount();
}
};
}
/**
* 写入图片到内存
*
* @param url 地址
* @param bitmap 图片
*/
public void saveBitmap(String url, Bitmap bitmap) {
lruCache.put(url, bitmap);
}
/**
* 从内存中拿到图片
*
* @param url 地址
*/
public Bitmap getBitmap(String url) {
return lruCache.get(url);
}
}
- 本地缓存工具类
/**
* 本地缓存工具类
* Created by zxp on 2016/6/17.
*/
public class LocalCacheUtils {
//文件村粗的路径
private static final String CACHE_PATH = Environment.getExternalStorageDirectory().getAbsolutePath() +
"/ImageCache";
/**
* 拿到本地存储的图片
*
* @param url 地址
* @return
*/
public Bitmap getBitmap(String url) {
try {
//截取文件名字
String fileName = url.substring(url.lastIndexOf("/") + , url.lastIndexOf("."));
File file = new File(CACHE_PATH, fileName);
Bitmap bitmap = BitmapFactory.decodeStream(new FileInputStream(file));
return bitmap;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 保存图片到本地
*
* @param url 地址
* @param bitmap 图片
*/
public void saveBitmap(String url, Bitmap bitmap) {
try {
String fileName = url.substring(url.lastIndexOf("/") + , url.lastIndexOf("."));
File file = new File(CACHE_PATH, fileName);
//判断父目录是否存在,不存在新建一个
File parentFile = file.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
//保存图片到本地,
// 注意第二个参数:如果不压缩是100,表示压缩率为0,如果是30,表示压缩70%;
bitmap.compress(Bitmap.CompressFormat.JPEG, , new FileOutputStream(file));
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 网路加载工具类
**
* 网络加载图片工具类
* 网络请求拿到的结果数据bitmap,再保存在本地和内存中
* Created by zxp on //
*/
public class NetBitmapUtils {
public static final int BITMAP_INSAMPLESIZE_ONE = ;//原图
public static final int BITMAP_INSAMPLESIZE_HALF = ;//宽高变为1/2
public static final int BITMAP_INSAMPLESIZE_ONE_THIRD = ;//宽高变为1/3
public static final int BITMAP_INSAMPLESIZE_ONE_QUARTER = ;//宽高变为1/4
private LocalCacheUtils localCacheUtils;
private MenoryCacheUtils menoryCacheUtils;
//图片压缩倍率(比如:1为不压缩,2为压缩宽高为一半)
private int compressNumber = ;
public NetBitmapUtils(MenoryCacheUtils menoryCacheUtils, LocalCacheUtils localCacheUtils) {
this.menoryCacheUtils = menoryCacheUtils;
this.localCacheUtils = localCacheUtils;
}
public NetBitmapUtils(MenoryCacheUtils menoryCacheUtils, LocalCacheUtils localCacheUtils, int compressNumber) {
this.menoryCacheUtils = menoryCacheUtils;
this.localCacheUtils = localCacheUtils;
this.compressNumber = compressNumber;
}
/**
* 网络下载图片
*
* @param iv 显示图片的imagview
* @param url 网络下载的地址
*/
public void getBitmap(ImageView iv, String url) {
getBitmap(iv, url, compressNumber);
}
/**
* 网络下载图片
*
* @param iv 显示图片的imagview
* @param url 网络下载的地址
* @param compressnumber 图片压缩倍率(1为不压缩)
*/
public void getBitmap(ImageView iv, String url, int compressnumber) {
this.compressNumber = compressnumber;
new ImageViewTask().execute(iv, url, compressnumber);
}
/**
* AsyncTask就是对handler和线程池的封装
* 第一个泛型:参数类型
* 第二个泛型:更新进度的泛型
* 第三个泛型:onPostExecute的返回结果
*/
class ImageViewTask extends AsyncTask<Object, Void, Bitmap> {
private ImageView iv;
private String url;
private int compressNumber;
//在后台做耗时操作,运行在子线程
@Override
protected Bitmap doInBackground(Object... params) {
iv = (ImageView) params[];
url = (String) params[];
compressNumber = (int) params[];
return downloadBitmap(url);
}
//更新进步,在主线程
@Override
protected void onProgressUpdate(Void... values) {
super.onProgressUpdate(values);
}
//耗时方法(请求网络)结束后执行该方法,在主线程中,带有请求结果的返回值
@Override
protected void onPostExecute(Bitmap bitmap) {
if (bitmap != null) {
iv.setImageBitmap(bitmap);
//网络拿到的图片保存到本地
localCacheUtils.saveBitmap(url, bitmap);
//网络拿到的图片保存到内存中
menoryCacheUtils.saveBitmap(url, bitmap);
}
}
private Bitmap downloadBitmap(String url) {
HttpURLConnection connection = null;
try {
connection = (HttpURLConnection) new URL(url).openConnection();
connection.setConnectTimeout();
connection.setReadTimeout();
connection.setRequestMethod("GET");
//请求成功
if (connection.getResponseCode() == ) {
BitmapFactory.Options options = new BitmapFactory.Options();
//图片压缩
if (compressNumber > ) {
//如果compressNumber为2,宽高压缩为原来的1/2
options.inSampleSize = compressNumber;
}
//ALPHA_8——代表8位Alpha位图
//ARGB_4444——代表16位ARGB位图
//ARGB_8888——代表32位ARGB位图
//RGB_565——代表8位RGB位图
options.inPreferredConfig = Bitmap.Config.ARGB_4444;
Bitmap bitmap = BitmapFactory.decodeStream(connection.getInputStream(), null, options);
return bitmap;
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
}
工具类使用方法
ImageLoaderUtils.getInstance().setImageViewFromBitmap(imageView , url);
图片宽高压缩为原来一半:
ImageLoaderUtils.getInstance().setImageViewFromBitmap(imageView , url , 2);
github分享地址
github的Demo