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

前言:对于图片的缓存现在都倾向于使用开源项目,如下:

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
智慧校园整体解决方案是响应国家教育信息化政策,结合教育改革和技术创新的产物。该方案以物联网、大数据、人工智能和移动互联技术为基础,旨在打造一个安全、高效、互动且环保的教育环境。方案强调从数字化校园向智慧校园的转变,通过自动数据采集、智能分析和按需服务,实现校园业务的智能化管理。 方案的总体设计原则包括应用至上、分层设计和互联互通,确保系统能够满足不同用户角色的需求,并实现数据和资源的整合与共享。框架设计涵盖了校园安全、管理、教学、环境等多个方面,构建了一个全面的校园应用生态系统。这包括智慧安全系统、校园身份识别、智能排课及选课系统、智慧学习系统、精品录播教室方案等,以支持个性化学习和教学评估。 建设内容突出了智慧安全和智慧管理的重要性。智慧安全管理通过分布式录播系统和紧急预案一键启动功能,增强校园安全预警和事件响应能力。智慧管理系统则利用物联网技术,实现人员和设备的智能管理,提高校园运营效率。 智慧教学部分,方案提供了智慧学习系统和精品录播教室方案,支持专业级学习硬件和智能化网络管理,促进个性化学习和教学资源的高效利用。同时,教学质量评估中心和资源应用平台的建设,旨在提升教学评估的科学性和教育资源的共享性。 智慧环境建设则侧重于基于物联网的设备管理,通过智慧教室管理系统实现教室环境的智能控制和能效管理,打造绿色、节能的校园环境。电子班牌和校园信息发布系统的建设,将作为智慧校园的核心和入口,提供教务、一卡通、图书馆等系统的集成信息。 总体而言,智慧校园整体解决方案通过集成先进技术,不仅提升了校园的信息化水平,而且优化了教学和管理流程,为学生、教师和家长提供了更加便捷、个性化的教育体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值