前言:
这里说的三级缓存,分别指的是:内存缓存、文件缓存和网络这三个层面。
一般来说,我们首次加载图片,内存和文件是没有缓存的,这样我们需要从网络加载,加载完成后,我们会存到内存和文件中去;当再次加载图片的时候,我们会先查找内存有没有,如果有就直接显示内存中的图片,如果没有,我们会接着查找文件中是否有,如果文件中有,我们会显示文件中的图片,并且把它存到内存中去,这样下次我们在内存中就能找到它了。
我们之所以要做缓存,主要是为了提高效率,节省流量。但是为什么要做三级呢?为什么不只存在内存或者只存在文件中呢?这是因为内存的读取速度快,但是容易被回收,容量小,文件的读取速度次之,不过容量大,不到不得已不会被回收。
有了以上的介绍,我们已经知道了三级缓存的必要性和实施步骤,接下来,我们就要选择在每级缓存的缓存策略了。
内存缓存,最开始大家推崇的是用SoftRefrence(软引用),它只有在内存不够的情况下才会被GC回收。但是高版本的安卓系统更倾向于回收SoftRefrence,这使得SoftRefrence不那么好用了。不过,安卓在3.0之后提供了LRUCache,它采用了最近最少使用的淘汰策略。本篇文章我们的内存缓存使用的就是LruCache.
文件缓存,我们使用的是DiskLruCache 点击这里下载
网络请求,这里我们使用Volley网络请求框架 点击这里下载。不懂的可以看这里 Android Volley入门到精通:定制自己的Request
内存缓存LruCache
// 获取应用可占内存的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();
}
};
文件缓存DiskLruCache
//DiskLruCache实例,它的构造方法是私有的,所以我们需要通过它提供的open方法来生成。
try {
mDiskLruCache = DiskLruCache.open(getDiskCacheDir(MyApplication.getContext(),CACHE_FOLDER_NAME),
getAppVersion(MyApplication.getContext()) , 1, DISKMAXSIZE);
} catch (IOException e) {
e.printStackTrace();
}
String diskKey = MD5Utils.md5(s);
try {
if(mDiskLruCache.get(diskKey) != null){ //文件中有
//从文件中取
Log.d(TAG,"从文件中取");
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(diskKey);
Bitmap bitmap = null;
if(snapshot != null){
bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));
//存入内存
mLruCache.put(s,bitmap);
}
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
//存入文件
String diskKey = MD5Utils.md5(s);
try {
if(mDiskLruCache.get(diskKey) == null){
Log.d(TAG,"存入文件");
DiskLruCache.Editor editor = mDiskLruCache.edit(diskKey);
if(editor != null){
OutputStream outputStream = editor.newOutputStream(0);
if(bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream)){
editor.commit();
}else{
editor.abort();
}
}
mDiskLruCache.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
下面是这个ImageCacheUtil类的全部
public class ImageCacheUtil implements ImageLoader.ImageCache {
//缓存类
private static LruCache<String, Bitmap> mLruCache;
private static DiskLruCache mDiskLruCache;
//磁盘缓存大小
private static final int DISKMAXSIZE = 10 * 1024 * 1024;
//路径
private static String CACHE_FOLDER_NAME = "YR_ImageCache";
private String TAG = ImageCacheUtil.class.getSimpleName();
public ImageCacheUtil() {
// 获取应用可占内存的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();
}
};
//DiskLruCache实例,它的构造方法是私有的,所以我们需要通过它提供的open方法来生成。
try {
mDiskLruCache = DiskLruCache.open(getDiskCacheDir(MyApplication.getContext(),CACHE_FOLDER_NAME),
getAppVersion(MyApplication.getContext()) , 1, DISKMAXSIZE);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* volley请求的时候会先回调getBitmap查看缓存中是否有图片,没有再去请求
* @param s
* @return
*/
@Override
public Bitmap getBitmap(String s) {
if(mLruCache.get(s) != null){ //内存中有
//从内存获取
Log.d(TAG,"从内存获取");
return mLruCache.get(s);
}else {
String diskKey = MD5Utils.md5(s);
try {
if(mDiskLruCache.get(diskKey) != null){ //文件中有
//从文件中取
Log.d(TAG,"从文件中取");
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(diskKey);
Bitmap bitmap = null;
if(snapshot != null){
bitmap = BitmapFactory.decodeStream(snapshot.getInputStream(0));
//存入内存
mLruCache.put(s,bitmap);
}
return bitmap;
}
} catch (IOException e) {
e.printStackTrace();
}
}
Log.d(TAG,"从网络中取");
return null;
}
/**
* 当Volley下载完图片后会来回调putBitmap方法来将图片进行缓存
* @param s
* @param bitmap
*/
@Override
public void putBitmap(String s, Bitmap bitmap) {
//存入内存
Log.d(TAG,"存入内存");
mLruCache.put(s,bitmap);
//存入文件
String diskKey = MD5Utils.md5(s);
try {
if(mDiskLruCache.get(diskKey) == null){
Log.d(TAG,"存入文件");
DiskLruCache.Editor editor = mDiskLruCache.edit(diskKey);
if(editor != null){
OutputStream outputStream = editor.newOutputStream(0);
if(bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream)){
editor.commit();
}else{
editor.abort();
}
}
mDiskLruCache.flush();
}
} catch (IOException e) {
e.printStackTrace();
}
}
//该方法会判断当前sd卡是否存在,然后选择缓存地址
public 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();
}
Log.d(TAG,cachePath + File.separator + uniqueName);
return new File(cachePath + File.separator + uniqueName);
}
//获得应用version号码
public int getAppVersion(Context context) {
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
return info.versionCode;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
}
return 1;
}
}
Volley下载图片
public class RequestQueueManager {
public static RequestQueue mRequestQueue = Volley.newRequestQueue(MyApplication.getContext());
public static void addRequest(Request<?> request, Object object){
if (object != null){
request.setTag(object);
}
mRequestQueue.add(request);
}
public static void cancelAll(Object tag) {
mRequestQueue.cancelAll(tag);
}
}
Volley给我们提供了ImageLoader类和ImageCache类用于图片下载。我们可以用ImageLoader的get(url,ImageLoader.ImageListeer,width,height)方法来下载图片
private static ImageCacheUtil mImagetCache = new ImageCacheUtil();
public static ImageLoader mImageLoader = new ImageLoader(RequestQueueManager.mRequestQueue,mImagetCache);
2.用get方法进行图片下载
public static void loadImage(String url,ImageLoader.ImageListener imageListener){
mImageLoader.get(url,imageListener,0,0);
}
public static void loadImage(String url,ImageLoader.ImageListener imageListener,int maxWidth,int maxHeight){
mImageLoader.get(url,imageListener,maxWidth,maxHeight);
}
3.在Activity中调用
ImageCacheManager.loadImage("http://img0.bdstatic.com/img/image/shouye/xiaoxiao/%E5%AE%A0%E7%89%A983.jpg",
new ImageLoader.ImageListener() {
@Override
public void onResponse(ImageLoader.ImageContainer imageContainer, boolean b) {
progressLy.setVisibility(View.GONE);
if(imageContainer.getBitmap() != null){
imageView.setImageBitmap(imageContainer.getBitmap());
}
}
@Override
public void onErrorResponse(VolleyError volleyError) {
progressLy.setVisibility(View.GONE);
}
});
好了,以上就是安卓三级缓存的实现。
下载源码点击这里