利用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,以上是主要代码,看下效果截图:



0 0