实现图片缓存也不难,需要有相应的cache策略。这里我采用 内存-文件-网络 三层cache机制,其中内存缓存包括强引用缓存和软引用缓存(SoftReference),其实网络不算cache,这里姑且也把它划到缓存的层次结构中

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(最近最久未使用缓存)会在后续文章中介绍。

/* 
* 图片管理 
* 异步获取图片,直接调用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加载图片的策略的更多相关文章

  1. iOS网络加载图片缓存策略之ASIDownloadCache缓存优化

    iOS网络加载图片缓存策略之ASIDownloadCache缓存优化   在我们实际工程中,很多情况需要从网络上加载图片,然后将图片在imageview中显示出来,但每次都要从网络上请求,会严重影响用 ...

  2. 图片--Android加载图片导致内存溢出(Out of Memory异常)

    Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory  Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证) ...

  3. Android加载图片OOM错误解决方式

    前几天做项目的时候,甲方要求是PAD (SAMSUNG P600 10.1寸 2560*1600)的PAD上显示高分辨率的大图片. SQLITE採用BOLD方式存储图片,这个存取过程就不说了哈,网上一 ...

  4. android 加载图片oom若干方案小结

    本文根据网上提供的一些技术方案加上自己实际开发中遇到的情况小结. 众所周知,每个Android应用程序在运行时都有一定的内存限制,限制大小一般为16MB或24MB(视手机而定).一般我们可以通过获取当 ...

  5. android 加载图片框架--Glide使用详解

    一.简介 Glide,一个被google所推荐的图片加载库,作者是bumptech.这个库被广泛运用在google的开源项目中,包括2014年的google I/O大会上发布的官方app.(PS:众所 ...

  6. Android加载图片导致内存溢出(Out of Memory异常)

    Android在加载大背景图或者大量图片时,经常导致内存溢出(Out of Memory  Error),本文根据我处理这些问题的经历及其它开发者的经验,整理解决方案如下(部分代码及文字出处无法考证) ...

  7. android 加载图片防止内存溢出

    图片资源: private int fore[]; private int back[]; fore = new int[]{R.drawable.a0, R.drawable.a1, R.drawa ...

  8. 解决android加载图片时内存溢出问题

    尽量不要使用setImageBitmap或setImageResource或BitmapFactory.decodeResource来设置一张大图,因为这些函数在完成decode后,最终都是通过jav ...

  9. Android加载图片小结

    应用中用到图片加载需要解决的问题 无网络环境下图片不可用 图片的本地缓存,或者默认预加载的图片 低配置机型,加载图像资源超内存(OutOfMemory, OoM) 需要合理使用内存,尤其是bitmap ...

随机推荐

  1. Python的GUI编程(TK)

    TK在大多数 Unix平台.Windows平台和Macintosh系统都是预装好的,TKinter 模块是 Tk GUI 套件的标准Python接口.可实现Python的GUI编程. Tkinter模 ...

  2. Spring Boot Redis Cluster 实战干货

    添加配置信息 spring.redis: database: 0 # Redis数据库索引(默认为0) #host: 192.168.1.8 #port: 6379 password: 123456 ...

  3. spring boot -thymeleaf-异常处理

    异常流程 :controller-ControllerAdvice-异常解析器-默认的error页面 1.错误页面error.html: 程序出现异常会自动跳到error.html里,视频效果,该页面 ...

  4. Google Chrome 书签导出并生成 MHTML 文件

    目的 因为某些原因需要将存放在 Google Chrome 内的书签导出到本地,所幸 Google Chrome 提供了导出书签的功能. 分析 首先在 Google Chrome 浏览器当中输入 ch ...

  5. mysql 开发基础系列21 事务控制和锁定语句(下)

    1.  隐含的执行unlock tables 如果在锁表期间,用start transaction命令来开始一个新事务,会造成一个隐含的unlock tables 被执行,如下所示: 会话1 会话2 ...

  6. Android--Service之AIDL传递系统基本类型数据

    前言 前面讲解了Service的一些基本内容.但是对于绑定服务传递数据,只局限于本地服务,无法使用服务进行跨进程间的交互.如果需要用到跨进程交互的话,需要用到一个新的技术-AIDL,这篇博客就针对AI ...

  7. ZOJ Problem Set - 3706

    #include <cstdio> #include <cstdlib> #include <cstring> #include <set> #incl ...

  8. javaweb之Filter过滤器详解

    快速入门 1.新建一个类,实现Filter接口 2.实现doFilter()方法,打印一句话,来证明能够进行拦截 3.在web.xml中进行配置(参照Servlet配置) 4.访问一个页面,看看能不能 ...

  9. 前端测试框架Jest系列教程 -- Expect(验证)

    写在前面 在编写测试时,我们通常需要检查值是否满足某些条件,Jest中提供的expect允许你访问很多“Matchers”,这些“匹配器”允许您验证不同的东西. Expect 可以验证什么 Jest中 ...

  10. 翻译:用户变量(User-Defined Variable)(已提交到MariaDB官方手册)

    本文为mariadb官方手册:User-Defined Variables的译文. 原文:https://mariadb.com/kb/en/user-defined-variables/我提交到Ma ...