实现图片缓存也不难,需要有相应的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. 国内使用google搜索引擎

    百度搜索 "谷歌访问助手",点击第一个搜索结果,如下: 或者直接点击链接:http://www.ggfwzs.com/  ,然后点击相应的浏览器下载谷歌访问助手,解压,将解压好的谷 ...

  2. Ansible系列之roles使用说明

    roles(角色)介绍 ansible自1.2版本开始引入的新特性,用于层次性,结构化地组织playbook.roles能够根据层次型结构自动装载变量文件.tasks以及handlers等.要使用ro ...

  3. 一个系统部署多个tomcat实例

    一.添加环境到/etc/profile. JAVA_HOME=/usr/java/jdk1.8.0_111 PATH=$JAVA_HOME/bin:$PATH CLASSPATH=.:$JAVA_HO ...

  4. linux取IP的几个方法

    ifconfig eth0|grep " inet add"|cut -d":" -f2|cut -d " " -f1 ifconfig e ...

  5. sql server 日志文件结构及误操作数据找回

    一. 概述 在sql server 里有数据文件.mdf和日志文件.ldf,日志文件是sqlserver数据库的另一个重要组成部分,日志文件记录了所有事务以及每个事务对数据库所做的修改.为了提高数据库 ...

  6. CentOS7.0小随笔——指令基本操作(Part.A)

    与其说是CentOS7.0的小随笔,说老实话,基本指令在每个发行版本的Linux中都基本上是一致的. Part.A部分我们讲述以下四个方面:命令行界面与图形界面.Linux系统的关闭与重启.命令行帮助 ...

  7. Hadoop学习笔记(三):java操作Hadoop

    1. 启动hadoop服务. 2. hadoop默认将数据存储带/tmp目录下,如下图: 由于/tmp是linux的临时目录,linux会不定时的对该目录进行清除,因此hadoop可能就会出现意外情况 ...

  8. .Net Core 读取配置文件 appsettings.json

    1. 首先些一个类 public class MySettings { public string P1 { get; set; } public string P2 { get; set; } } ...

  9. (10)Microsoft office Word 2013版本操作入门_word表格

    1.套用word模板  :点击[文件]---[新建]---选择合适模板创建即可. word中插入[书法字帖]: 2.插入表格 :点击[插入]---[表格]输入行和列 ,固定列宽为“自动”时 默认沾满左 ...

  10. java_GPS数据处理

    题目内容: NMEA-0183协议是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine ...