在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量。对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理,每次打开应用都去网络获取图片,那么用户可就不乐意了,这里的处理就是指今天要讲的缓存策略(缓存层分为三层:内存层,磁盘层,网络层)。

  关于缓存层的工作,当我们第一次打开应用获取图片时,先到网络去下载图片,然后依次存入内存缓存,磁盘缓存,当我们再一次需要用到刚才下载的这张图片时,就不需要再重复的到网络上去下载,直接可以从内存缓存和磁盘缓存中找,由于内存缓存速度较快,我们优先到内存缓存中寻找该图片,如果找到则运用,如果没有找到(内存缓存大小有限),那么我们再到磁盘缓存中去找。只要我们合理的去协调这三层缓存运用,便可以提升应用性能和用户体验。

 此博文源码下载地址  https://github.com/Javen205/VolleyDemo.git

1、内存层:(手机内存)

内存缓存相对于磁盘缓存而言,速度要来的快很多,但缺点容量较小且会被系统回收,这里的实现我用到了LruCache。

LruCache这个类是Android3.1版本中提供的,如果你是在更早的Android版本中开发,则需要导入android-support-v4的jar包。

磁盘层:(SD卡)

相比内存缓存而言速度要来得慢很多,但容量很大,这里的实现我用到了DiskLruCache类。

DiskLruCache是非Google官方编写,但获得官方认证的硬盘缓存类,该类没有限定在Android内,所以理论上java应用也可以使用DiskLreCache来缓存。

这是DiskLruCache类的下载地址:http://pan.baidu.com/s/1o6tPjz8

网络层:(移动网络,无线网络)

这个就没什么解释的了,就是我们上网用的流量。这里的网络访问实现我用到了开源框架Volley。

开源框架Volley是2013年Google I/O大会发布的,Volley是Android平台上的网络通信库,能使网络通信更快,更简单,更健壮。它的设计目标就是非常适合去进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会非常糟糕。

这是Volley的下载地址:http://pan.baidu.com/s/1kThedX9

以下是代码实现:

先附上两个工具类 


生成MD5序列帮助类,DiskLruCache磁盘缓存类(下载地址见上文)

import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException; public class MD5Utils {
/**
* 使用md5的算法进行加密
*/
public static String md5(String plainText) {
byte[] secretBytes = null;
try {
secretBytes = MessageDigest.getInstance("md5").digest(
plainText.getBytes());
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("没有md5这个算法!");
}
String md5code = new BigInteger(1, secretBytes).toString(16);// 16进制数字
// 如果生成数字未满32位,需要前面补0
for (int i = 0; i < 32 - md5code.length(); i++) {
md5code = "0" + md5code;
}
return md5code;
} }
com.android.volley.toolbox.ImageLoader.ImageLoader(RequestQueue queue, ImageCache imageCache)
我们可以看到Volley加载图片需要一个请求队列以及图片的缓存机制
 
 
图片缓存类ImageCacheUtil 包含(LruCache内存缓存,DiskLruCache磁盘缓存)
 
package com.javen.volley.cache;

import java.io.File;
import java.io.IOException;
import java.io.OutputStream; import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.BitmapFactory;
import android.os.Environment;
import android.support.v4.util.LruCache;
import android.util.Log; import com.android.volley.toolbox.ImageLoader.ImageCache;
import com.javen.volley.cache.DiskLruCache.Snapshot; /**
* 图片缓存帮助类
* 包含内存缓存LruCache和磁盘缓存DiskLruCache
* @author Javen
*/
public class ImageCacheUtil implements ImageCache { private String TAG=ImageCacheUtil.this.getClass().getSimpleName(); //缓存类
private static LruCache<String, Bitmap> mLruCache; private static DiskLruCache mDiskLruCache;
//磁盘缓存大小
private static final int DISKMAXSIZE = 10 * 1024 * 1024; public ImageCacheUtil(Context context) {
// 获取应用可占内存的1/8作为缓存
int maxSize = (int) (Runtime.getRuntime().maxMemory() / 8);
// 实例化LruCaceh对象
mLruCache = new LruCache<String, Bitmap>(maxSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();
}
};
try {
// 获取DiskLruCahce对象
// mDiskLruCache = DiskLruCache.open(getDiskCacheDir(MyApplication.newInstance(), "xxxxx"), getAppVersion(MyApplication.newInstance()), 1, DISKMAXSIZE);
mDiskLruCache = DiskLruCache.open(getDiskCacheDir(context.getApplicationContext(), "xxxxx"), getAppVersion(context), 1, DISKMAXSIZE);
} catch (IOException e) {
e.printStackTrace();
}
} /**
* 从缓存(内存缓存,磁盘缓存)中获取Bitmap
*/
@Override
public Bitmap getBitmap(String url) {
if (mLruCache.get(url) != null) {
// 从LruCache缓存中取
Log.i(TAG,"从LruCahce获取");
return mLruCache.get(url);
} else {
String key = MD5Utils.md5(url);
try {
if (mDiskLruCache.get(key) != null) {
// 从DiskLruCahce取
Snapshot snapshot = mDiskLruCache.get(key);
Bitmap bitmap = null;
if (snapshot != null) {
bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));
// 存入LruCache缓存
mLruCache.put(url, bitmap);
Log.i(TAG,"从DiskLruCahce获取");
}
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
} /**
* 存入缓存(内存缓存,磁盘缓存)
*/
@Override
public void putBitmap(String url, Bitmap bitmap) {
// 存入LruCache缓存
mLruCache.put(url, bitmap);
// 判断是否存在DiskLruCache缓存,若没有存入
String key = MD5Utils.md5(url);
try {
if (mDiskLruCache.get(key) == null) {
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if (editor != null) {
OutputStream outputStream = editor.newOutputStream(0);
if (bitmap.compress(CompressFormat.JPEG, 100, outputStream)) {
editor.commit();
} else {
editor.abort();
}
}
mDiskLruCache.flush();
}
} catch (IOException e) {
e.printStackTrace();
} } /**
* 该方法会判断当前sd卡是否存在,然后选择缓存地址
*
* @param context
* @param uniqueName
* @return
*/
public static File getDiskCacheDir(Context context, String uniqueName) {
String cachePath;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()) || !Environment.isExternalStorageRemovable()) {
cachePath = context.getExternalCacheDir().getPath();
} else {
cachePath = context.getCacheDir().getPath();
}
return new File(cachePath + File.separator + uniqueName);
} /**
* 获取应用版本号
*
* @param context
* @return
*/
public int getAppVersion(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return info.versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return 1;
} }

Volley请求队列处理类,用来管理Rquest请求对象操作

package com.javen.volley;

import android.content.Context;
import android.text.TextUtils; import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;
import com.javen.volley.cache.ImageCacheUtil; public class VolleyController { // 创建一个TAG,方便调试或Log
private static final String TAG = "VolleyController"; // 创建一个全局的请求队列
private RequestQueue reqQueue;
private ImageLoader imageLoader; // 创建一个static VolleyController对象,便于全局访问
private static VolleyController mInstance; private Context mContext; private VolleyController(Context context) {
mContext=context;
} /**
* 以下为需要我们自己封装的添加请求取消请求等方法
*/ // 用于返回一个VolleyController单例
public static VolleyController getInstance(Context context) {
if (mInstance == null) {
synchronized(VolleyController.class)
{
if (mInstance == null) {
mInstance = new VolleyController(context);
}
}
}
return mInstance;
} // 用于返回全局RequestQueue对象,如果为空则创建它
public RequestQueue getRequestQueue() {
if (reqQueue == null){
synchronized(VolleyController.class)
{
if (reqQueue == null){
reqQueue = Volley.newRequestQueue(mContext);
}
}
}
return reqQueue;
} public ImageLoader getImageLoader(){
getRequestQueue();
//如果imageLoader为空则创建它,第二个参数代表处理图像缓存的类
if(imageLoader==null){
//LruCache
//imageLoader=new ImageLoader(reqQueue, new LruBitmapCache());
//LruCache DiskLruCache
imageLoader=new ImageLoader(reqQueue, new ImageCacheUtil(mContext));
}
return imageLoader;
} /**
* 将Request对象添加进RequestQueue,由于Request有*StringRequest,JsonObjectResquest...
* 等多种类型,所以需要用到*泛型。同时可将*tag作为可选参数以便标示出每一个不同请求
*/ public <T> void addToRequestQueue(Request<T> req, String tag) {
// 如果tag为空的话,就是用默认TAG
req.setTag(TextUtils.isEmpty(tag) ? TAG : tag); getRequestQueue().add(req);
} public <T> void addToRequestQueue(Request<T> req) {
req.setTag(TAG);
getRequestQueue().add(req);
} // 通过各Request对象的Tag属性取消请求
public void cancelPendingRequests(Object tag) {
if (reqQueue != null) {
reqQueue.cancelAll(tag);
}
}
}

图片缓存管理类(方便外部调用)

这里向外部提供了一个loadImage的重载方法,一个传入加载图片的宽高,一个默认加载原图,使得外部不再需要关注任何关于缓存的操作。

package com.javen.volley.utils;

import android.content.Context;
import android.graphics.Bitmap;
import android.widget.ImageView;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageLoader.ImageContainer;
import com.android.volley.toolbox.ImageLoader.ImageListener;
import com.javen.volley.VolleyController; /**
* 图片缓存管理类 获取ImageLoader对象
* @author Javen
*
*/
public class ImageCacheManager {
private static String TAG = ImageCacheManager.class.getSimpleName(); /**
* 获取ImageListener
*
* @param view
* @param defaultImage
* @param errorImage
* @return
*/
public static ImageListener getImageListener(final ImageView view, final Bitmap defaultImage, final Bitmap errorImage) { return new ImageListener() { @Override
public void onErrorResponse(VolleyError error) {
// 回调失败
if (errorImage != null) {
view.setImageBitmap(errorImage);
}
} @Override
public void onResponse(ImageContainer response, boolean isImmediate) {
// 回调成功
if (response.getBitmap() != null) {
view.setImageBitmap(response.getBitmap());
} else if (defaultImage != null) {
view.setImageBitmap(defaultImage);
}
}
}; } /**
* 提供给外部调用方法
*
* @param url
* @param view
* @param defaultImage
* @param errorImage
*/
public static void loadImage(Context context,String url, ImageView view, Bitmap defaultImage, Bitmap errorImage) {
VolleyController.getInstance(context).getImageLoader().get(url, ImageCacheManager.getImageListener(view, defaultImage, errorImage), 0, 0);
} /**
* 提供给外部调用方法
*
* @param url
* @param view
* @param defaultImage
* @param errorImage
*/
public static void loadImage(Context context,String url, ImageView view, Bitmap defaultImage, Bitmap errorImage, int maxWidth, int maxHeight) {
VolleyController.getInstance(context).getImageLoader().get(url, ImageCacheManager.getImageListener(view, defaultImage, errorImage), maxWidth, maxHeight);
}
}

使用方法

   public void CacheImage(View view){
Bitmap defaultImage=BitmapFactory.decodeResource(getResources(), R.drawable.loading);
Bitmap errorImage=BitmapFactory.decodeResource(getResources(), R.drawable.load_error);
ImageCacheManager.loadImage(this, url, imageView, defaultImage, errorImage);
}

转:http://blog.csdn.net/jie1991liu/article/details/46926421

Android Volley框架的使用(四)图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)的更多相关文章

  1. 安卓开发笔记——关于图片的三级缓存策略(内存LruCache+磁盘DiskLruCache+网络Volley)

    在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...

  2. 网络图片的获取以及二级缓存策略(Volley框架+内存LruCache+磁盘DiskLruCache)

    在开发安卓应用中避免不了要使用到网络图片,获取网络图片很简单,但是需要付出一定的代价——流量.对于少数的图片而言问题不大,但如果手机应用中包含大量的图片,这势必会耗费用户的一定流量,如果我们不加以处理 ...

  3. Android中图片的三级缓存策略

    在开发过程中,经常会碰到进行请求大量的网络图片的样例.假设处理的不好.非常easy造成oom.对于避免oom的方法,无非就是进行图片的压缩.及时的回收不用的图片.这些看似简单可是处理起来事实上涉及的知 ...

  4. Android应用框架中的四个核心要点

    Android应用框架中的四个核心要点:活动(Activity).消息(Intent).视图(View).任务(Task) (一)活动Activity Android系统内部有专门的Activity堆 ...

  5. Android中图片的三级缓存

    为什么要使用三级缓存 如今的 Android App 经常会需要网络交互,通过网络获取图片是再正常不过的事了 假如每次启动的时候都从网络拉取图片的话,势必会消耗很多流量.在当前的状况下,对于非wifi ...

  6. # Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析#

    Volley源码解析(二) 没有缓存的情况下直接走网络请求源码分析 Volley源码一共40多个类和接口.除去一些工具类的实现,核心代码只有20多个类.所以相对来说分析起来没有那么吃力.但是要想分析透 ...

  7. Android View框架总结(四)View布局流程之Measure

    View树的measure流程 View的measures时序图 View布局流程之measure measure过程 View的measure过程 ViewGroup的measure过程 Frame ...

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

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

  9. 简单地Android中图片的三级缓存机制

    我们不能每次加载图片的时候都让用户从网络上下载,这样不仅浪费流量又会影响用户体验,所以Android中引入了图片的缓存这一操作机制. 原理: 首先根据图片的网络地址在网络上下载图片,将图片先缓存到内存 ...

随机推荐

  1. Java坦克大战 (七) 之图片版

    本文来自:小易博客专栏.转载请注明出处:http://blog.csdn.net/oldinaction 在此小易将坦克大战这个项目分为几个版本,以此对J2SE的知识进行回顾和总结,希望这样也能给刚学 ...

  2. javascript写的轮播图

    欢迎指点! 先放上效果图: 鼠标移入界面后: <body onselectstart="return false;" style="-moz-user-select ...

  3. OpenCV-2.4.6-android-sdk 人脸识别demo搭建

    最近项目需要研究下人脸识别,在领导推荐下准备研究OpenCV 一,上官网了解下 基本知识 http://docs.opencv.org/doc/tutorials/introduction/andro ...

  4. 正则表达式筛选出jpg、png的图片url

    有些字符串也不是富文本,也不是带标准标签的图片地址和文字.想筛选出所有图片或地址怎么办呢.话不多说直接上带码. private static void reg() { // TODO Auto-gen ...

  5. C#关于log4net(Log For Net)

    1       介绍 log4net(Log For Net)是Apache开源的应用于.Net框架的日志记录工具,详细信息参见Apache网站.它是针对Java的log4j(Log For Java ...

  6. centos6.5 安装stardict 出现问题 [Errno 256] No more mirrors to try

    1. 下载startdict #wget http://downloads.naulinux.ru/pub/NauLinux/6x/i386/sites/School/RPMS/stardict-3. ...

  7. Error converting bytecode to dex: Cause: java.lang.RuntimeException: Exception parsing classes

    http://blog.csdn.net/xx326664162/article/details/51859106 总算有个靠谱的了

  8. 树上两点的最近公共祖先问题(Least Common Ancestors)

    概念: 对于有根树T的两个节点u,v,最近公共祖先LCA(T, u, v)表示一个节点 x, 满足 x 是 u , v 的祖先且 x 的深度尽可能的大.即从 u 到 v 的路径一定经过点 x. 算法: ...

  9. tarjan算法与无向图的连通性(割点,桥,双连通分量,缩点)

    基本概念 给定无向连通图G = (V, E)割点:对于x∈V,从图中删去节点x以及所有与x关联的边之后,G分裂为两个或两个以上不相连的子图,则称x为割点割边(桥)若对于e∈E,从图中删去边e之后,G分 ...

  10. 读《thinking in java》第一遍笔记

    1.System.out.println(variable)为什么会调用重写的toString方法? 根据我搜到的信息结合源代码,得到以下结论,如有误请告知. 首先我们看一下println方法的源代码 ...