android中图片的三级缓存cache策略(内存/文件/网络)
android中图片的三级缓存cache策略(内存/文件/网络)
发布时间:2016-12-28 来源:查字典编辑
摘要: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;

}

}

推荐文章
猜你喜欢
附近的人在看
推荐阅读
拓展阅读
相关阅读
网友关注
最新安卓软件开发学习
热门安卓软件开发学习
编程开发子分类