Android网络图片的三级缓存
2015-09-20 22:36
579 查看
1.缓存原理
实现图片缓存也不难,需要有相应的cache策略。这里我采用内存-文件-网络三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中。当根据url向网络拉取图片的时候,先从内存中找,如果内存中没有,再从缓存文件中查找,如果缓存文件中也没有,再从网络上通过http请求拉取图片。在键值对(key-value)中,这个图片缓存的key是图片url的hash值,value就是bitmap。所以,按照这个逻辑,只要一个url被下载过,其图片就被缓存起来了。关于Java中对象的软引用(SoftReference),如果一个对象具有软引用,内存空间足够,垃 圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高 速缓存。使用软引用能防止内存泄露,增强程序的健壮性。
2.代码
<pre name="code" class="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; } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories