Android 图片的三级缓存机制实例分析
Android图片的三级缓存机制实例分析
当我们获取图片的时候,如果不加以协调好图片的缓存,就会造成大流量,费流量应用,用户体验不好,影响后期发展。为此,我特地分享Android图片的三级缓存机制之从网络中获取图片,来优化应用,具体分三步进行:
(1)从缓存中获取图片
(2)从本地的缓存目录中获取图片,并且获取到之后,放到缓存中
(3)从网络去下载图片,下载完成之后,保存到本地和放到缓存中
很好的协调这三层图片缓存就可以大幅度提升应用的性能和用户体验。
快速实现三级缓存的工具类ImageCacheUtil如下(有更好的建议可以发我的邮箱说出你的想法,一起完善,邮箱见博客主页“给我写信”):
1.从网络中获取图片的三级缓存工具类ImageCacheUtil
publicclassImageCacheUtil{
privateLruCachelruCache;
privateFilecacheDir;
privateExecutorServicenewFixedThreadPool;
privateHandlerhandler;
publicstaticfinalintSUCCESS=100;
publicstaticfinalintFAIL=101;
//当我们获取图片的时候,分三步
//1.从缓存中获取图片
//2.从本地的缓存目录中获取图片,并且获取到之后,放到缓存中
//3.从网络去下载图片,下载完成之后,保存到本地缓存目录和放到缓存中
publicImageCacheUtil(Contextcontext,Handlerhandler){
//获取缓存的大小
intmaxsize=(int)(Runtime.getRuntime().maxMemory()/8);
//maxSize:设置缓存的最大空间
lruCache=newLruCache(maxsize){
//获取移出的图片所占用的空间,当图片移出的时候,需要将图片占用的缓存空间从缓存中移出
@Override
protectedintsizeOf(Stringkey,Bitmapvalue){
//计算图片所占用的缓存大小
//getRowBytes:获取图片一行所占用的大小
//getHeight:获取图片所占用行数
returnvalue.getRowBytes()*value.getHeight();
}
};
//获取缓存目录
cacheDir=context.getCacheDir();
//获取线程池
//nThreads:线程池中的线程数量
newFixedThreadPool=Executors.newFixedThreadPool(5);
this.handler=handler;
}
/**
*获取图片的方法
*@paramurl
*@parampositon
*@return
*/
publicBitmapgetBitmap(Stringurl,intposition){
Bitmapbitmap=null;
//1.从缓存中获取图片(LRUCache)k:key保存图片的标示,一般都是图片的url地址,v:value图片
bitmap=lruCache.get(url);//根据key从缓存中获取相应的图片
//lruCache.put(url,bitmap):保存图片到缓存中
if(bitmap!=null){
returnbitmap;
}
//2.从本地的缓存目录中获取图片,并且获取到之后,放到缓存中
bitmap=getFromLocal(url);
if(bitmap!=null){
returnbitmap;
}
//3.从网络去下载图片,下载完成之后,保存到本地缓存目录和放到缓存中
getFromNet(url,position);
returnnull;
}
/**
*从网络下载图片,异步方式,线程池
*@paramurl
*@paramposition
*/
privatevoidgetFromNet(Stringurl,intposition){
newFixedThreadPool.execute(newRunnableTask(url,position));
}
classRunnableTaskimplementsRunnable{
privateStringimageUrl;
privateintposition;
publicRunnableTask(Stringurl,intposition){
this.imageUrl=url;
this.position=position;
}
@Override
publicvoidrun(){
Messagemessage=Message.obtain();
//下载图片
try{
URLurl=newURL(imageUrl);
HttpURLConnectionconn=(HttpURLConnection)url.openConnection();
conn.setReadTimeout(3000);
conn.setConnectTimeout(5000);
conn.setRequestMethod("GET");
InputStreaminputStream=conn.getInputStream();
Bitmapbitmap=BitmapFactory.decodeStream(inputStream);
//保存到本地缓存中
wirteToLocal(imageUrl,bitmap);
//保存到系统缓冲中
lruCache.put(imageUrl,bitmap);
//显示图片,给handler发送消息
message.what=SUCCESS;
message.obj=bitmap;
message.arg1=position;
handler.sendMessage(message);
return;
}catch(Exceptione){
e.printStackTrace();
}
message.what=FAIL;
handler.sendMessage(message);
}
}
/**
*从本地缓存目录获取图片
*@paramurl
*/
privateBitmapgetFromLocal(Stringurl){
//根据图片的名称获取图片
try{
StringfileName=MD5Encoder.encode(url).substring(10);
Filefile=newFile(cacheDir,fileName);
Bitmapbitmap=BitmapFactory.decodeFile(file.getAbsolutePath());
//防盗缓存当中
lruCache.put(url,bitmap);
returnbitmap;
}catch(Exceptione){
e.printStackTrace();
}
returnnull;
}
/**
*将图片保存到本地缓存目录中
*/
publicvoidwirteToLocal(Stringurl,Bitmapbitmap){
//url名称,通过MD5加密,并且截取前10位作为名称
try{
StringfileName=MD5Encoder.encode(url).substring(10);
Filefile=newFile(cacheDir,fileName);
FileOutputStreamout=newFileOutputStream(file);
//format:图片的格式(android中用的png多,因为png质量是不会改变的)
//quality:压缩比例
//stream:流信息
bitmap.compress(CompressFormat.JPEG,100,out);//将图片保存到那个位置
}catch(Exceptione){
e.printStackTrace();
}
}
}
其中用到的MD5Encoder类如下:
publicclassMD5Encoder{
publicstaticStringencode(Stringstring)throwsException{
byte[]hash=MessageDigest.getInstance("MD5").digest(string.getBytes("UTF-8"));
StringBuilderhex=newStringBuilder(hash.length*2);
for(byteb:hash){
if((b&0xFF)<0x10){
hex.append("0");
}
hex.append(Integer.toHexString(b&0xFF));
}
returnhex.toString();
}
}
2.在MainActivity初始化imageCacheUtil
ImageCacheUtilimageCacheUtil=newImageCacheUtil(getApplicationContext,handler);
3.在MainActivity中通过handler将图片显示出来
图片通过工具类下载成功之后,不仅要将图片保存到本地缓存中和系统缓存中,还要将图片显示出来,通过handler进行处理,这个handler是设置使用ImageCacheUtil工具类,就要把你的handler传递过来,方便我们传消息给相应使用ImageCacheUtil工具类的类进行处理。
privateHandlerhandler=newHandler(){
publicvoidhandleMessage(android.os.Messagemsg){
switch(msg.what){
caseImageCacheUtil.SUCCESS:
//给控件设置图片
Bitmapbitmap=(Bitmap)msg.obj;
intposition=msg.arg1;
ImageViewimage=(ImageView)view.findViewWithTag(position);//就是根据条目的位置获取相应的控件
if(image!=null&&bitmap!=null){
image.setImageBitmap(bitmap);
}
break;
caseImageCacheUtil.FAIL:
Toast.makeText(getApplicationContext,"图片下载失败",0).show();
break;
}
};
};
4.在MainActivity中的adapter的getview中进行调用
Bitmapbitmap=imageCacheUtil.getBitmap(list.get(position).listimage,position);
if(bitmap!=null){
viewHodler.image.setImageBitmap(bitmap);
}
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!