欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

android中图片的三级缓存cache策略(内存/文件/网络)

程序员文章站 2023-11-04 14:45:46
1.简介 现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。现在...
1.简介
现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多。

现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量。在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响。当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略。总之,图片缓存是很重要而且是必须的。

2.图片缓存的原理
实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(softreference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。

关于java中对象的软引用(softreference),如果一个对象具有软引用,内存空间足够,垃 圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,增强程序的健壮性。

从代码上来说,采用一个imagemanager来负责图片的管理和缓存,函数接口为public void loadbitmap(string url, handler handler) ;其中url为要下载的图片地址,handler为图片下载成功后的回调,在handler中处理message,而message中包含了图片的信息以及bitmap对象。imagemanager中使用的imagememorycache(内存缓存)、imagefilecache(文件缓存)以及lrucache(最近最久未使用缓存)会在后续文章中介绍。

3.代码imagemanager.java
复制代码 代码如下:

/*
* 图片管理
* 异步获取图片,直接调用loadimage()函数,该函数自己判断是从缓存还是网络加载
* 同步获取图片,直接调用getbitmap()函数,该函数自己判断是从缓存还是网络加载
* 仅从本地获取图片,调用getbitmapfromnative()
* 仅从网络加载图片,调用getbitmapfromhttp()
*
*/
public class imagemanager implements imanager
{
private final static string tag = "imagemanager";

private imagememorycache imagememorycache; //内存缓存

private imagefilecache imagefilecache; //文件缓存

//正在下载的image列表
public static hashmap<string, handler> ongoingtaskmap = new hashmap<string, handler>();

//等待下载的image列表
public static hashmap<string, handler> waitingtaskmap = new hashmap<string, handler>();

//同时下载图片的线程个数
final static int max_download_image_thread = 4;

private final handler downloadstatushandler = new handler(){
public void handlemessage(message msg)
{
startdownloadnext();
}
};

public imagemanager()
{
imagememorycache = new imagememorycache();
imagefilecache = new imagefilecache();
}

/**
* 获取图片,多线程的入口
*/
public void loadbitmap(string url, handler handler)
{
//先从内存缓存中获取,取到直接加载
bitmap bitmap = getbitmapfromnative(url);
if (bitmap != null)
{
logger.d(tag, "loadbitmap:loaded from native");
message msg = message.obtain();
bundle bundle = new bundle();
bundle.putstring("url", url);
msg.obj = bitmap;
msg.setdata(bundle);
handler.sendmessage(msg);
}
else
{
logger.d(tag, "loadbitmap:will load by network");
downloadbmponnewthread(url, handler);
}
}
/**
* 新起线程下载图片
*/
private void downloadbmponnewthread(final string url, final handler handler)
{
logger.d(tag, "ongoingtaskmap'size=" + ongoingtaskmap.size());

if (ongoingtaskmap.size() >= max_download_image_thread)
{
synchronized (waitingtaskmap)
{
waitingtaskmap.put(url, handler);
}
}
else
{
synchronized (ongoingtaskmap)
{
ongoingtaskmap.put(url, handler);
}
new thread()
{
public void run()
{
bitmap bmp = getbitmapfromhttp(url);
// 不论下载是否成功,都从下载队列中移除,再由业务逻辑判断是否重新下载
// 下载图片使用了httpclientrequest,本身已经带了重连机制
synchronized (ongoingtaskmap)
{
ongoingtaskmap.remove(url);
}

if(downloadstatushandler != null)
{
downloadstatushandler.sendemptymessage(0);

}
message msg = message.obtain();
msg.obj = bmp;
bundle bundle = new bundle();
bundle.putstring("url", url);
msg.setdata(bundle);

if(handler != null)
{
handler.sendmessage(msg);
}
}
}.start();
}
}
/**
* 依次从内存,缓存文件,网络上加载单个bitmap,不考虑线程的问题
*/
public bitmap getbitmap(string url)
{
// 从内存缓存中获取图片
bitmap bitmap = imagememorycache.getbitmapfrommemory(url);
if (bitmap == null)
{
// 文件缓存中获取
bitmap = imagefilecache.getimagefromfile(url);
if (bitmap != null)
{
// 添加到内存缓存
imagememorycache.addbitmaptomemory(url, bitmap);
}
else
{
// 从网络获取
bitmap = getbitmapfromhttp(url);
}
}
return bitmap;
}

/**
* 从内存或者缓存文件中获取bitmap
*/
public bitmap getbitmapfromnative(string url)
{
bitmap bitmap = null;
bitmap = imagememorycache.getbitmapfrommemory(url);

if(bitmap == null)
{
bitmap = imagefilecache.getimagefromfile(url);
if(bitmap != null)
{
// 添加到内存缓存
imagememorycache.addbitmaptomemory(url, bitmap);
}
}
return bitmap;
}

/**
* 通过网络下载图片,与线程无关
*/
public bitmap getbitmapfromhttp(string url)
{
bitmap bmp = null;

try
{
byte[] tmppicbyte = getimagebytes(url);

if (tmppicbyte != null)
{
bmp = bitmapfactory.decodebytearray(tmppicbyte, 0,
tmppicbyte.length);
}
tmppicbyte = null;
}
catch(exception e)
{
e.printstacktrace();
}

if(bmp != null)
{
// 添加到文件缓存
imagefilecache.savebitmaptofile(bmp, url);
// 添加到内存缓存
imagememorycache.addbitmaptomemory(url, bmp);
}
return bmp;
}

/**
* 下载链接的图片资源
*
* @param url
*
* @return 图片
*/
public byte[] getimagebytes(string url)
{
byte[] pic = null;
if (url != null && !"".equals(url))
{
requester request = requesterfactory.getrequester(
requester.request_remote, requesterfactory.impl_hc);
// 执行请求
myresponse myresponse = null;
myrequest mmyrequest;
mmyrequest = new myrequest();
mmyrequest.seturl(url);
mmyrequest.addheader(httpheader.req.accept_encoding, "identity");
inputstream is = null;
bytearrayoutputstream baos = null;
try {
myresponse = request.execute(mmyrequest);
is = myresponse.getinputstream().getimpl();
baos = new bytearrayoutputstream();
byte[] b = new byte[512];
int len = 0;
while ((len = is.read(b)) != -1)
{
baos.write(b, 0, len);
baos.flush();
}
pic = baos.tobytearray();
logger.d(tag, "icon bytes.length=" + pic.length);
}
catch (exception e3)
{
e3.printstacktrace();
try
{
logger.e(tag,
"download shortcut icon faild and responsecode="
+ myresponse.getstatuscode());
}
catch (exception e4)
{
e4.printstacktrace();
}
}
finally
{
try
{
if (is != null)
{
is.close();
is = null;
}
}
catch (exception e2)
{
e2.printstacktrace();
}
try
{
if (baos != null)
{
baos.close();
baos = null;
}
}
catch (exception e2)
{
e2.printstacktrace();
}
try
{
request.close();
}
catch (exception e1)
{
e1.printstacktrace();
}
}
}
return pic;
}

/**
* 取出等待队列第一个任务,开始下载
*/
private void startdownloadnext()
{
synchronized(waitingtaskmap)
{
logger.d(tag, "begin start next");
iterator iter = waitingtaskmap.entryset().iterator();

while (iter.hasnext())
{

map.entry entry = (map.entry) iter.next();
logger.d(tag, "waitingtaskmap isn't null,url=" + (string)entry.getkey());

if(entry != null)
{
waitingtaskmap.remove(entry.getkey());
downloadbmponnewthread((string)entry.getkey(), (handler)entry.getvalue());
}
break;
}
}
}

public string startdownloadnext_forunittest()
{
string urlstring = null;
synchronized(waitingtaskmap)
{
logger.d(tag, "begin start next");
iterator iter = waitingtaskmap.entryset().iterator();

while (iter.hasnext())
{
map.entry entry = (map.entry) iter.next();
urlstring = (string)entry.getkey();
waitingtaskmap.remove(entry.getkey());
break;
}
}
return urlstring;
}

/**
* 图片变为圆角
* @param bitmap:传入的bitmap
* @param pixels:圆角的度数,值越大,圆角越大
* @return bitmap:加入圆角的bitmap
*/
public static bitmap toroundcorner(bitmap bitmap, int pixels)
{
if(bitmap == null)
return null;
bitmap output = bitmap.createbitmap(bitmap.getwidth(), bitmap.getheight(), config.argb_8888);
canvas canvas = new canvas(output);
final int color = 0xff424242;
final paint paint = new paint();
final rect rect = new rect(0, 0, bitmap.getwidth(), bitmap.getheight());
final rectf rectf = new rectf(rect);
final float roundpx = pixels;
paint.setantialias(true);
canvas.drawargb(0, 0, 0, 0);
paint.setcolor(color);
canvas.drawroundrect(rectf, roundpx, roundpx, paint);
paint.setxfermode(new porterduffxfermode(mode.src_in));
canvas.drawbitmap(bitmap, rect, rect, paint);
return output;
}

public byte managerid()
{
return image_id;
}
}