android中图片的三级cache策略(内存、文件、网络)
来源:互联网 发布:淘宝假酒处罚规定 编辑:程序博客网 时间:2024/05/23 19:14
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;}}
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略 .
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之二:内存缓存策略
- android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略
- android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略
- android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略
- android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略
- android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略
- android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略 .
- android中图片的三级cache策略(内存、文件、网络)之三:文件缓存策略
- android中图片的三级cache策略(内存、文件、网络) 一
- android中图片的三级cache策略(内存、文件、网络) 一
- windows上搭建hadoop开发环境
- error LNK2019: 无法解析的外部符号 __imp_DeleteObject
- Android 自定义View (二) 进阶
- windows下安装apache2.4+mysql5.7+php7遇到的问题ww
- mysql-cluster集群原理介绍和搭建步骤(四个data/sql节点)
- android中图片的三级cache策略(内存、文件、网络)
- NFV主要概念的术语
- Jquery Post提交时Content-Type的不同取值详解
- 在linux下安装软件,经常就是装完后不知道装到哪里去了
- Maven 添加 JAR 包到本地仓库
- android4.0屏蔽导航按钮的方法
- Java 注解 拦截器
- Java类的反射机制
- java 文件的过滤 FileFilter