您的位置:首页 > 其它

利用LruCache和DiskLruCache实现图片异步下载

2015-10-24 20:39 936 查看
前言:对于图片的缓存现在都倾向于使用开源项目,如下:

1. Android-Universal-Image-Loader 图片缓存

目前使用最广泛的图片缓存,支持主流图片缓存的绝大多数特性。

项目地址:https://github.com/nostra13/Android-Universal-Image-Loader

2. picasso square开源的图片缓存

项目地址:https://github.com/square/picasso

特点:(1)可以自动检测adapter的重用并取消之前的下载

(2)图片变换

(3)可以加载本地资源

(4)可以设置占位资源

(5)支持debug模式

3. ImageCache 图片缓存,包含内存和Sdcard缓存

项目地址:https://github.com/Trinea/AndroidCommon

特点:

(1)支持预取新图片,支持等待队列

(2)包含二级缓存,可自定义文件名保存规则

(3)可选择多种缓存算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13种)或自定义缓存算法

(4)可方便的保存及初始化恢复数据

(5)支持不同类型网络处理

(6)可根据系统配置初始化缓存等

4.以及FaceBook新近开源的图片下载框架Fresco

本教程适合新手,能力有限,如有错误欢迎指出;

开始撸码:

核心:LruCache和DiskLruCache以及线程池

1. LruCache进行内存缓存(Google官方提供)

2. DiskLruCache硬盘缓存(非Google官方提供,已经获得官方认证)源码地址:https://github.com/JakeWharton/DiskLruCache

本文代码是在郭神以及这位大神的基础上修改而成,感谢两位大神!

LruCache和DiskLruCache用法参考以上两位大神;

项目:



代码API和Android-Universal-Image-Loader类似

package cc.mnbase.image;

import android.content.Context;
import android.content.pm.PackageInfo;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.ThumbnailUtils;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import cc.mnbase.image.libcore.io.DiskLruCache;

/**
* Date: 2015-10-22
* Time: 10:22
* Version 1.0
*/

public class ImageLoader {

private LruCache<String, Bitmap> mMemoryCache; //内存缓存

private DiskLruCache mDiskLruCache; //google官方推荐处理本地缓存库

private ExecutorService mDownlodThreadPool;  //下载图片线程池

private int maxDiskMemory = 20*1024*1024; //本地最大缓存:20M

private FileUtils fileUtils;

private Context mContext;

private String tag = ImageLoader.class.getSimpleName();

public ImageLoader(Context context){
mContext = context;
//获取系统为每个应用分配的最大内存
int maxMemory = (int)Runtime.getRuntime().maxMemory();
int mCacheSize = maxMemory / 8;
//分配最大内存的1/8
mMemoryCache = new LruCache<String, Bitmap>(mCacheSize){
@Override
protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
}

@Override
protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
//可以进行强制回收oldValue
//oldValue.recycle();
//oldValue = null;
}
};
fileUtils = new FileUtils(context);
try {
mDiskLruCache = DiskLruCache.open(new File(fileUtils.getStorageDirectory()), getAppVersion(context),
1, maxDiskMemory);
} catch (IOException e) {
e.printStackTrace();
}

}

/**
* 获取线程池
* */
// private ExecutorService getThreadPool(){
//     if(mDownlodThreadPool==null){
//         synchronized (ExecutorService.class){
//             if(mDownlodThreadPool==null){
//                 mDownlodThreadPool = Executors.newFixedThreadPool(1);
//             }
//         }
//     }
//      return mDownlodThreadPool;
//  }
/**
* 获取线程池
* */
private ExecutorService getThreadPool(){
if(mDownlodThreadPool==null){
mDownlodThreadPool = Executors.newFixedThreadPool(5);
}
return mDownlodThreadPool;
}

/**
* 添加Bitmap到内存缓存中
* */
private void addBitmapToMemoryCache(String key, Bitmap value){
if(getBitmapFromMemoryCache(key) == null && value!=null){
mMemoryCache.put(key, value);
}
}

/**
* 从内存缓存中获取Bitmap
* */
private Bitmap getBitmapFromMemoryCache(String key){
return mMemoryCache.get(key);
}

/**
* 下载图片
* @param imageView 图片控件
* @param listener  回调方法
* @param url 图片地址
* */
public void displayImage(String url, ImageView imageView, onImageLoaderListener listener){
downLoadImage(url, null, imageView, null, listener, null);
}

/**
* 下载图片
* @param imageSize 设置图片尺寸
* @param imageView 图片控件
* @param listener 回调方法
* @param url 图片地址
* */
public void loadImage(String url, ImageSize imageSize, ImageView imageView, DisplayImageOptions options, onImageLoaderListener listener,
onImageLoadingProgressListener progressListener){
downLoadImage(url, imageSize, imageView, options, listener, progressListener);
}

/**
* 下载图片
* @param imageSize 设置图片尺寸
* @param imageView 图片控件
* @param listener 回调方法
* @param url 图片地址
* */
public void loadImage(String url, ImageSize imageSize, ImageView imageView, onImageLoaderListener listener,
onImageLoadingProgressListener progressListener){
loadImage(url, imageSize, imageView, null, listener, progressListener);
}

/**
* 下载图片
* @param imageSize 设置图片尺寸
* @param imageView 图片控件
* @param listener 回调方法
* @param url 图片地址
* */
public void loadImage(String url, ImageSize imageSize, ImageView imageView, onImageLoaderListener listener){
loadImage(url, imageSize, imageView, listener, null);
}

/**
* 下载图片
* @param imageSize 设置图片尺寸
* @param imageView 图片控件
* @param url 图片地址
* */
public void loadImage(String url, ImageSize imageSize, ImageView imageView){
loadImage(url, imageSize, imageView, null);
}

/**
* 下载图片
* @param imageView 图片控件
* @param url 图片地址
* */
public void loadImage(String url, ImageView imageView){
loadImage(url, null, imageView);
}

/**
* 下载图片
* */
private void downLoadImage(final String url, final ImageSize imageSize, final ImageView imageView, final DisplayImageOptions options,
final onImageLoaderListener listener, final onImageLoadingProgressListener progressListener){
if(options!=null){
imageView.setImageDrawable(options.getImageOnLoading(mContext.getResources()));
}
final String key = MD5.hashKeyForDisk(url);
Bitmap bitmap = showCacheBitmap(key);
if(bitmap!=null){
if(imageSize!=null){
bitmap = createNewBitmap(bitmap, imageSize);
}
if(imageView!=null){
imageView.setImageBitmap(bitmap);
}
if(listener!=null){
listener.onImageLoader(bitmap, url);
}
return;
} else {
final int[] fileLength = {0};
final Handler handler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 0:
if(imageView!=null){
Bitmap tmpBitmap = (Bitmap)msg.obj;
if(tmpBitmap!=null){
imageView.setImageBitmap(tmpBitmap);
if(listener!=null){
listener.onImageLoader(tmpBitmap, url);
}
} else {
if(options!=null){
imageView.setImageDrawable(options.getImageOnFail(mContext.getResources()));
}
}
}
break;
case 1:
if(progressListener!=null){
progressListener.onProgressUpdate(url, imageView, (int)msg.obj, fileLength[0]);
}
break;
case 2:
fileLength[0] = (int)msg.obj;
break;
}
}
};

getThreadPool().execute(new Runnable() {
@Override
public void run() {
Bitmap bitmap = saveBitmapToDisk(url, key, handler);  //下载并保存在储存卡中
if(bitmap!=null && imageSize!=null){
bitmap = createNewBitmap(bitmap, imageSize);
}
Message msg = handler.obtainMessage(0, bitmap);
handler.sendMessage(msg);

addBitmapToMemoryCache(key, bitmap); //添加Bitmap到手机内存里
}
});

}
}

/**
* 根据ImageSize重新设置图片尺寸
* */
private Bitmap createNewBitmap(Bitmap bitmap, ImageSize imageSize){
//系统提供的图片缩放方法
Bitmap tmp = ThumbnailUtils.extractThumbnail(bitmap, imageSize.getWidth(), imageSize.getHeight());
return tmp;
}

/**
* 从内存或者SD卡中获取图片
* */
private Bitmap showCacheBitmap(String key){
if(getBitmapFromMemoryCache(key) != null){
return getBitmapFromMemoryCache(key);
} else {
Bitmap bitmap = getBitmapFromDisk(key);
addBitmapToMemoryCache(key, bitmap);
return bitmap;
}
}

/**
* 从储存卡缓存中获取Bitmap
* */
private Bitmap getBitmapFromDisk(String key){
Bitmap bitmap = null;
try{
DiskLruCache.Snapshot snapshot = mDiskLruCache.get(key);
if(snapshot!=null){
InputStream is = snapshot.getInputStream(0);
bitmap = BitmapFactory.decodeStream(is);
}
} catch (Exception e){
e.printStackTrace();
}
return bitmap;
}

/**
* 获取储存卡已缓存图片的大小
* */
public int getDiskMemory(){
return mMemoryCache.size();
}

/**
* 保存Bitmap到储存卡中
* */
private Bitmap saveBitmapToDisk(String url, String key, Handler handler){
Bitmap bitmap = null;
try{
DiskLruCache.Editor editor = mDiskLruCache.edit(key);
if(editor!=null){
OutputStream os = editor.newOutputStream(0);
boolean succ = getBitmapFromUrl(url, key, handler, os);
if(succ){
editor.commit(); //一定要执行此方法否则不会保存图片
bitmap = getBitmapFromDisk(key); //从SD卡获取
} else {
editor.abort();
}
}
mDiskLruCache.flush();
} catch (Exception e){
e.printStackTrace();
}
return bitmap;
}

/**
* 从网络中获取图片
* */
private boolean getBitmapFromUrl(String url, String key,  Handler handler, OutputStream fos){
boolean succ = false;
HttpURLConnection con = null;
try{
URL imgUrl = new URL(url);
con = (HttpURLConnection)imgUrl.openConnection();
con.setConnectTimeout(10 * 1000);
con.setReadTimeout(10 * 1000);

InputStream is = con.getInputStream();

int fileLength = con.getContentLength();
Message message = handler.obtainMessage(2, fileLength);
handler.sendMessage(message);
byte buf[] = new byte[1024];
int downLoadFileSize = 0;
do{
int numread = is.read(buf);
if(numread == -1){
break; //终止循环
}
fos.write(buf, 0, numread);
downLoadFileSize += numread;
Message msg = handler.obtainMessage(1, downLoadFileSize);
handler.sendMessage(msg);
} while (true);

succ = true; //下载完成

} catch (Exception e){
e.printStackTrace();
succ = false;
} finally {
if(con != null){
con.disconnect();
}
}

return succ;
}

/**
* 取消下载任务
* */
//public synchronized void cancelTask(){
//    if(mDownlodThreadPool != null){
//        mDownlodThreadPool.shutdownNow();
//        mDownlodThreadPool = null;
//    }
//}
/**
* 取消下载任务
* */
public void cancelTask(){
if(mDownlodThreadPool != null){
mDownlodThreadPool.shutdownNow();
mDownlodThreadPool = null;
}
}

/**
* 获取app当前版本号
* */
private int getAppVersion(Context context){
try {
PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 1);
return info.versionCode;
} catch (Exception e){
e.printStackTrace();
}
return 1;
}

/**
* 图片异步下载的回调方法
* */
public interface onImageLoaderListener{
void onImageLoader(Bitmap bitmap, String url);
}

/**
* 图片异步下载的进度回调方法
* */
public interface onImageLoadingProgressListener{
void onProgressUpdate(String imageUri, View view, int current, int total);
}

}


FileUtil类主要是获取图片缓存路径:

import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Build;
import android.os.Environment;
import android.util.Log;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

/**
* Date: 2015-10-22
* Time: 11:49
* Version 1.0
*/

public class FileUtils {

public static String mSdRootPath = Environment.getExternalStorageDirectory().getPath();

private static String mDataRootPath = null;

private final static String APP_PACKAGE_FOLDER = "/Android/data";

private final static String FOLDER_NAME = "/image";

private String appPackage = null;

private String tag = FileUtils.class.getSimpleName();

public FileUtils(Context context){
appPackage = context.getPackageName();
mDataRootPath = context.getCacheDir().getPath();
}

/**
* 获取图片保存路径:优先保存在SD卡
* */
public String getStorageDirectory(){
String path = Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED) ?
mSdRootPath + APP_PACKAGE_FOLDER + File.separator + appPackage + FOLDER_NAME : mDataRootPath + FOLDER_NAME;
File folderFile = new File(path);
if(!folderFile.exists()){ //目录不存在时,进行创建
folderFile.mkdirs();
}
return path;
}
}




ImageSize类主要为了设置图片大小:

package cc.mnbase.image;

/**
* Date: 2015-10-22
* Time: 14:57
* Version 1.0
*/

public class ImageSize {

private int width;

private int height;

public ImageSize(int width, int height){
this.width = width;
this.height = height;
}

public int getWidth() {
return width;
}

public void setWidth(int width) {
this.width = width;
}

public int getHeight() {
return height;
}

public void setHeight(int height) {
this.height = height;
}

}


DisplayImageOptions类主要设置图片在下载中,失败时显示的默认图片(和Android-Universal-Image-Loader同名类用法类似):

package cc.mnbase.image;

import android.content.res.Resources;
import android.graphics.drawable.Drawable;

/**
* Date: 2015-10-22
* Time: 21:55
* Version 1.0
*/

public final class DisplayImageOptions {

private final int imageResOnLoading;

private final int imageResForEmptyUri;

private final int imageResFail;

private final Drawable imageLoading;

private final Drawable imageForEmptyUri;

private final Drawable imageOnFail;

private DisplayImageOptions(DisplayImageOptions.Builder builder){
this.imageResOnLoading = builder.imageResOnLoading;
this.imageResForEmptyUri = builder.imageResForEmptyUri;
this.imageResFail = builder.imageResOnFail;
this.imageLoading = builder.imageLoading;
this.imageForEmptyUri = builder.imageForEmptyUri;
this.imageOnFail = builder.imageOnFail;
}

public Drawable getImageOnLoading(Resources res){
return this.imageResOnLoading != 0 ?  res.getDrawable(this.imageResOnLoading):this.imageLoading;
}

public Drawable getImageForEmptyUri(Resources res){
return this.imageResForEmptyUri != 0 ? res.getDrawable(this.imageResForEmptyUri):this.imageForEmptyUri;
}

public Drawable getImageOnFail(Resources res){
return this.imageResFail != 0 ? res.getDrawable(this.imageResFail):this.imageOnFail;
}

public static class Builder{
private int imageResOnLoading = 0;
private int imageResForEmptyUri = 0;
private int imageResOnFail = 0;
private Drawable imageLoading = null;
private Drawable imageForEmptyUri = null;
private Drawable imageOnFail = null;

public Builder(){

}

/**
* 设置正在加载时显示图片
* */
public DisplayImageOptions.Builder showImageOnLoading(int imageRes){
this.imageResOnLoading = imageRes;
return this;
}

/**
*
* */
public DisplayImageOptions.Builder showImageForEmptyUri(int imageRes){
this.imageResForEmptyUri = imageRes;
return this;
}

/**
* 设置图片加载失败时显示的图片
* */
public DisplayImageOptions.Builder showImageOnFail(int imageRes){
this.imageResOnFail = imageRes;
return this;
}

/**
* 设置正在加载时显示图片
* */
public DisplayImageOptions.Builder showImageOnLoading(Drawable drawable){
this.imageLoading = drawable;
return this;
}

/**
*
* */
public DisplayImageOptions.Builder showImageForEmptyUri(Drawable drawable){
this.imageForEmptyUri = drawable;
return this;
}

/**
* 设置图片加载失败时显示的图片
* */
public DisplayImageOptions.Builder showImageOnFail(Drawable drawable){
this.imageOnFail = drawable;
return this;
}

public DisplayImageOptions build(){
return new DisplayImageOptions(this);
}

}

}


MD5类主要是处理图片名称:

package cc.mnbase.image;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
* Date: 2015-10-23
* Time: 15:22
* Version 1.0
*/

public class MD5 {

public static String hashKeyForDisk(String key){
String cacheKey = null;
try {
MessageDigest mDigest = MessageDigest.getInstance("MD5");
mDigest.update(key.getBytes());
cacheKey = byteToHexString(mDigest.digest());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
cacheKey = String.valueOf(key.hashCode());
}
return cacheKey;
}

private static String byteToHexString(byte[] bytes){
StringBuilder sb = new StringBuilder();

for(int i=0; i< bytes.length; i++){
String hex = Integer.toHexString(0XFF & bytes[i]);
if(hex.length() == 1){
sb.append('0');
}
sb.append(hex);
}

return sb.toString();
}
}
OK,以上是主要代码,看下效果截图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: