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

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;

}

}

时间: 2024-09-23 01:58:51

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

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

1.简介 现在android应用中不可避免的要使用图片,有些图片是可以变化的,需要每次启动时从网络拉取,这种场景在有广告位的应用以及纯图片应用(比如百度美拍)中比较多. 现在有一个问题:假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响.当然,我想,向百度美拍这样的应用,必然也有其内部的图片缓存策略.总之,图片缓存是很重要而且是必须的. 2.图片缓存的原理 实现图片缓存也不难,需要有相

Android中图片的三级缓存机制_Android

我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中 也就是LruCache中.如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softReference)中存储,然后将图片缓存到文件(内部存储外部存储)中:读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则

Android中图片的三级缓存机制

我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存缓存中,缓存到强引用中 也就是LruCache中.如果强引用中空间不足,就会将较早存储的图片对象驱逐到软引用(softReference)中存储,然后将图片缓存到文件(内部存储外部存储)中:读取图片的时候,先读取内存缓存,判断强引用中是否存在图片,如果强引用中存在,则直接读取,如果强引用中不存在,则

详解Android中图片的三级缓存及实例

详解Android中图片的三级缓存及实例 为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi用户来说,流量还是很贵的,一个很耗流量的应用,其用户数量级肯定要受到影响 特别是,当我们想要重复浏览一些图片时,如果每一次浏览都需要通过网络获取,流量的浪费可想而知 所以提出三级缓存策略,通过网络.本地.内存三级缓存图片,来减少不必要的网络交互,避免浪费流量

浅谈Android 中图片的三级缓存策略

什么是三级缓存? 内存缓存,优先加载,速度最快 本地缓存,次优先加载,速度快 网络缓存,最后加载,速度慢,浪费流量 为什么要进行三级缓存 三级缓存策略,最实在的意义就是 减少不必要的流量消耗,增加加载速度 . 如今的 APP 网络交互似乎已经必不可少,通过网络获取图片再正常不过了.但是,每次启动应用都要从网络获取图片,或者是想重复浏览一些图片的时候,每次浏览都需要网络获取,消耗的流量就多了,在如今的流量资费来说,肯定会容易影响用户数量. 还有就是网络加载图片,有时候会加载很慢,影响了用户体验.

Android中Rxjava实现三级缓存的两种方式

本文正如标题所说的用rxjava实现数据的三级缓存分别为内存,磁盘,网络,刚好最近在看Android源码设计模式解析与实战(受里面的ImageLoader的设计启发). 我把代码放到了我的hot项目中,github地址 源码下载地址:Rxjava_jb51.rar 1.使用concat()和first()的操作符. 2.使用BehaviorSubject. 先说BehaviorSubject的实现方法,废话不多说直接上代码, /** * Created by wukewei on 16/6/20

Android使用缓存机制实现文件下载及异步请求图片加三级缓存_Android

首先给大家介绍Android使用缓存机制实现文件下载 在下载文件或者在线浏览文件时,或者为了保证文件下载的正确性,需要使用缓存机制,常使用SoftReference来实现. SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收.也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用.另外

详解Android 图片的三级缓存及图片压缩_Android

为什么需要图片缓存 Android默认给每个应用只分配16M的内存,所以如果加载过多的图片,为了防止内存溢出,应该将图片缓存起来.图片的三级缓存分别是: 内存缓存 本地缓存 网络缓存 其中,内存缓存应优先加载,它速度最快:本地缓存次优先加载,它速度也快:网络缓存不应该优先加载,它走网络,速度慢且耗流量. 三级缓存的具体实现 网络缓存 根据图片的url去加载图片 在本地和内存中缓存    public class NetCacheUtils { private LocalCacheUtils mL

详解Android 图片的三级缓存及图片压缩

为什么需要图片缓存 Android默认给每个应用只分配16M的内存,所以如果加载过多的图片,为了防止内存溢出,应该将图片缓存起来.图片的三级缓存分别是: 内存缓存 本地缓存 网络缓存 其中,内存缓存应优先加载,它速度最快:本地缓存次优先加载,它速度也快:网络缓存不应该优先加载,它走网络,速度慢且耗流量. 三级缓存的具体实现 网络缓存 根据图片的url去加载图片 在本地和内存中缓存 public class NetCacheUtils { private LocalCacheUtils mLoca