当前位置:  编程技术>移动开发

android中图片的三级缓存cache策略(内存/文件/网络)

    来源: 互联网  发布时间:2014-10-18

    本文导语:  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 ongoingTaskMap = new HashMap();

//等待下载的image列表
public static HashMap waitingTaskMap = new HashMap();

//同时下载图片的线程个数
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(/tech-mobile-dev/url/index.html);
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开发教程之清除android数据缓存示例(清除本地数据缓存)
  • Android 缓存框架 ASimpleCache
  • android异步加载图片并缓存到本地实现方法
  • Android中加载网络资源时的优化可使用(线程+缓存)解决
  • android上的一个网络接口和图片缓存框架enif简析
  • android开发教程之用命令启动android模拟器并设置其内存大小
  • Android图片占用内存全面分析
  • android 退出程序解决内存释放的问题
  • android内存及内存溢出分析详解
  • Android编程中避免内存泄露的方法总结
  • Android加载大分辨率图片到手机内存中的实例方法
  • Android 中对于图片的内存优化方法
  • android内存优化之图片优化
  • Android 异步获取网络图片并处理导致内存溢出问题解决方法
  • 解析Android开发优化之:对Bitmap的内存优化详解
  • android实用工具类分享(获取内存/检查网络/屏幕高度/手机分辨率)
  • 解析Android获取系统cpu信息,内存,版本,电量等信息的方法详解
  • Android App调试内存泄露之Cursor篇
  •  
    本站(WWW.)旨在分享和传播互联网科技相关的资讯和技术,将尽最大努力为读者提供更好的信息聚合和浏览方式。
    本站(WWW.)站内文章除注明原创外,均为转载、整理或搜集自网络。欢迎任何形式的转载,转载请注明出处。












  • 相关文章推荐
  • Web服务器/前端 iis7站长之家
  • Android生存指南之:解Bug策略与思路问题的详解
  • 申请Android Map 的API Key(v2)的最新申请方式(SHA1密钥)
  • Android瀑布流实例 android_waterfall
  • Android开发需要的几点注意事项总结
  • Android系统自带样式 (android:theme)
  • android 4.0 托管进程介绍及优先级和回收机制
  • Android网络共享软件 Android Wifi Tether
  • Android访问与手机通讯相关类的介绍
  • Android 图标库 Android GraphView
  • Android及andriod无线网络Wifi开发的几点注意事项
  • 轻量级Android开发工具 Android Tools
  • Android 2.3 下StrictMode介绍
  • Android 开发环境 Android Studio
  • IDEA的Android开发插件 idea-android
  • Android手机事件提醒 Android Notifier
  • XBMC的Android客户端 android-xbmcremote
  • Android小游戏 Android Shapes
  • Android电池监控 Android Battery Dog
  • android开发:“android:WindowTitle”没有对应项no resource
  • Android 上类似IOS 的开关控件。 Android ToggleButton
  • Android 将 android view 的位置设为右下角的解决方法
  • Android 2D游戏引擎 Android Angle


  • 站内导航:


    特别声明:169IT网站部分信息来自互联网,如果侵犯您的权利,请及时告知,本站将立即删除!

    ©2012-2021,,E-mail:www_#163.com(请将#改为@)

    浙ICP备11055608号-3