利用LruCache和DiskLruCache实现图片异步下载
来源:互联网 发布:腾讯自制网络剧2017 编辑:程序博客网 时间:2024/06/05 04:37
前言:对于图片的缓存现在都倾向于使用开源项目,如下:
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,以上是主要代码,看下效果截图:
- 利用LruCache和DiskLruCache实现图片异步下载
- 使用LruCache和DiskLruCache来下载图片
- 利用LruCache和DiskLruCache加载网络图片实现图片瀑布流效果(升级版)
- 利用DiskLruCache 实现异步加载图片
- 利用LruCache和DiskLruCache使用图片加载的三级缓存
- AndroidStudio利用DiskLruCache和LruCache实现简单的照片墙
- 使用LruCache和DiskLruCache实现内存磁盘二级图片缓存
- 【LruCache和DiskLruCache结合】图片缓存机制
- 结合LruCache和DiskLruCache高效加载图片
- 图片的LruCache缓存和DiskLruCache缓存
- 【LruCache和DiskLruCache结合】图片缓存机制
- Android相册,利用LruCache、DiskLruCache实现图片的加载,避免出现OOM
- 图片缓存-LruCache、DiskLruCache
- 使用lrucache和diskLrucache实现照片墙
- LruCache,DiskLruCache实现分析
- Android使用LruCache、DiskLruCache实现图片缓存+图片瀑布流
- LruCache和DiskLruCache
- android图片加载防止OOM LruCache和DiskLruCache使用
- C# 数组的使用
- hdu 3943 数位dp+二分
- 求最大公约数(欧几里得算法)
- 谈java多线程方式刷数据的一点建议
- 用宏换参
- 利用LruCache和DiskLruCache实现图片异步下载
- 利用Visio转换成pdf图形时的注意事项
- 测试实习生面试题
- 拥抱未来吧——第二日
- vim 兼容性的bug --softtabstops
- Bootstrap插件carousel源码的学习
- WIFI项目--Android 获取本机IP(wifi下),子网掩码,网关,dns等信息
- hdu1532 网络流入门 dinic
- 参加公司招聘笔试有感