天天看点

LruCache和DiskLruCache实现二级缓存的自定义ImageLoader

图片的异步加载,缓存应该是每个开发者都会遇到也会用到的技术。目前有很多优秀的开源框架,要说最被广泛的使用的话,应该是Universal-Image-Loader,很多应用反编译一下,都会看到他的身影。UIL的缓存原理:

LruCache和DiskLruCache实现二级缓存的自定义ImageLoader

引用:http://www.cnblogs.com/kissazi2/p/3931400.html

LruCache是android自带的缓存类,Lru的全称是Least Recently Used ,即近期最少使用,LruCache的原理就是当内存不够时,删除最少使用的数据。我们可以用LruCache和DiskLruCache自己实现一个。

思路:图片先从内存中去取,内存中没有的话,再从硬盘中取,再没有的话,从网上异步下载,压缩后,保存到硬盘和内存中。为了保证流畅性和节约内存,我们一般都不会直接去加载下载的原图,而是对其进行缩放。下面是图片处理的工具类。

package com.example.zet.joker;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import java.io.FileDescriptor;

/**
 * Created by sean on 16/3/13.
 */
public class ImageUtil {

    public ImageUtil() {}

    public Bitmap decodeSampleBiemapFormFileDescroptor(FileDescriptor descriptor, int outWidth, int outHeight) {
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeFileDescriptor(descriptor, null, options);
        options.inSampleSize = calculateInSampleSize(options, outWidth, outHeight);
        options.inJustDecodeBounds = false;
        return BitmapFactory.decodeFileDescriptor(descriptor, null, options);
    }

    public int calculateInSampleSize(BitmapFactory.Options options, int outWidth, int outHeight) {
        if (outHeight ==  || outWidth == ) {
            return ;
        }
        int width = options.outWidth;
        int height = options.outHeight;
        int inSampleSize = ;
        if (height > outHeight || width > outWidth) {
            int halfHeight = height / ;
            int halfWidth = width / ;
            while ((halfHeight / inSampleSize) >= outHeight && (halfWidth / inSampleSize) >= outWidth) {
                inSampleSize *= ;
            }
        }
        return inSampleSize;
    }
}
           

LruCache的初始化方法:

//获取应用的内存
 int maxMemory = (int) (Runtime.getRuntime().maxMemory() / );
 //缓存的大小
 int size = maxMemory /;

 lruCache = new LruCache<String, Bitmap>(size) {
            @Override
            protected int sizeOf(String key, Bitmap value){
                return value.getRowBytes() * value.getHeight() / ;
            }
        };
           

我们只要重写 sizeOf 方法就行了,计算biemap的大小。

DiskLruCache的初始化方法:

//sd卡缓存
        long size =  *  * ;
        boolean sdCardExist = Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED);
        File diskDir;
        if (!sdCardExist)
            diskDir = new File(Environment.getRootDirectory() + "/sean/");
        else
            diskDir = new File(Environment.getExternalStorageDirectory() + "/sean/");
        if (!diskDir.exists()) {
            diskDir.mkdirs();
        }
        try {
            diskLruCache = DiskLruCache.open(diskDir, , , size);
        } catch (IOException e) {
            e.printStackTrace();
        }
           

贴起来好麻烦,代码全放上吧,看注释

LruCache和DiskLruCache实现二级缓存的自定义ImageLoader
public class ImageLoader {

    private static ImageLoader imageLoader;
    private static final String TAG = "ImageLoader";

    //线程池的配置
    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    private static final int CORE_POOL_SIZE = CPU_COUNT + ;
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT *  + ;
    private static final int KEEP_ALIVE = ;

    //SD卡的缓存大小
    private final static long DISK_SIZE =  *  * ;
    private final int IO_BUFFER_SIZE =  * ;

    private LruCache<String, Bitmap> lruCache;
    private DiskLruCache diskLruCache;

    private Context context;
    private ImageUtil imageUtil = new ImageUtil();
    ;

    private static final ThreadFactory factory = new ThreadFactory() {

        private final AtomicInteger mCount = new AtomicInteger();

        @Override
        public Thread newThread(Runnable r) {
            return new Thread(r, "thread: " + mCount.getAndIncrement());
        }
    };

    public static ImageLoader getInstance(Context context) {
        if (null == imageLoader) {
            imageLoader = new ImageLoader(context);
        }
        return imageLoader;
    }

    //初始化线程池
    public static final Executor THREAD_POOL_EXECUTOR = new ThreadPoolExecutor
            (CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(), factory);

    public ImageLoader(Context context) {
        context = context.getApplicationContext();
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / );
        int size = maxMemory / ;

        lruCache = new LruCache<String, Bitmap>(size) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount() / ;
            }
        };
        // 判断是否有SD卡
        boolean sdCardExist = Environment.getExternalStorageState().equals(
                android.os.Environment.MEDIA_MOUNTED);
        File diskDir;
        if (!sdCardExist) {
            diskDir = new File(Environment.getRootDirectory()
                    + "/sean/");
        } else {
            diskDir = new File(Environment.getExternalStorageDirectory()
                    + "/sean/");
        }
        if (!diskDir.exists()) {
            diskDir.mkdirs();
        }
        try {
            diskLruCache = DiskLruCache.open(diskDir, , , DISK_SIZE);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 异步加载图片的方法
     *
     * @param url
     * @param imageView
     * @param outWidth
     * @param outHeight
     */
    public void bindBitmap(final String url, final ImageView imageView, final int outWidth, final int outHeight) {
        imageView.setTag(url);
        //将地址转成MD5编码 方便存储和获取 因为有些地址含有特殊符号
        String key = hashKeyFromUrl(url);
        //先去内存中查找
        Bitmap bitmap = getBitmapFromMemroy(key);
        if (null != bitmap) {
            imageView.setImageBitmap(bitmap);
            return;
        }
        //开始异步获取
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                Bitmap bitmap1 = loadBitmap(url, outWidth, outHeight);
                if (null != bitmap1) {
                    LoaderResult result = new LoaderResult(imageView, bitmap1, url);
                    Message msg = mMainHandler.obtainMessage(, result);
                    msg.sendToTarget();
                }
            }
        };
        THREAD_POOL_EXECUTOR.execute(runnable);
    }

    /**
     * 从SD卡中查找缓存 没有的话 开始下载
     * @param url
     * @param outWidth
     * @param outHeight
     * @return
     */
    private Bitmap loadBitmap(String url, int outWidth, int outHeight) {
        try {
            String key = hashKeyFromUrl(url);
            //SD卡中查找
            Bitmap bitmap = loadBitmapDiskCache(key, outWidth, outHeight);
            if (null != bitmap) {
                return bitmap;
            }
            //冲网络上下载
            bitmap = loadBitmapFromHttp(url, outWidth, outHeight);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 存入内存
     *
     * @param key
     * @param bitmap
     */
    private void addBitmapToMemoryCache(String key, Bitmap bitmap) {
        if (null == getBitmapFromMemroy(key)) {
            lruCache.put(key, bitmap);
        }
    }

    /**
     * 内存取
     *
     * @param key
     */
    private Bitmap getBitmapFromMemroy(String key) {
        return lruCache.get(key);
    }


    /**
     * 本地缓存
     *
     * @param url
     */
    private Bitmap loadBitmapFromHttp(String url, int outWidth, int outHeight) {
        try {
            String key = hashKeyFromUrl(url);
            DiskLruCache.Editor editor = diskLruCache.edit(key);
            if (editor != null) {
                OutputStream outputStream = editor.newOutputStream();
                if (dowanloadUriToStream(url, outputStream)) {
                    editor.commit();
                } else {
                    editor.abort();
                }
            }
            diskLruCache.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return loadBitmapDiskCache(url, outWidth, outHeight);
    }

    /**
     * 本地缓存 读取
     * @param outWidth
     * @param outHeight
     * @return
     */
    private Bitmap loadBitmapDiskCache(String key, int outWidth, int outHeight) {
        Bitmap bitmap = null;
        try {
            DiskLruCache.Snapshot snapshot = diskLruCache.get(key);
            if (snapshot != null) {
                FileInputStream fileInputStream = (FileInputStream) snapshot.getInputStream();
                FileDescriptor fileDescriptor = fileInputStream.getFD();
                //更具需要的大小 进行压缩
                bitmap = imageUtil.decodeSampleBiemapFormFileDescroptor(fileDescriptor, outWidth, outHeight);
                if (null != bitmap) {
                    //加入lruCache
                    addBitmapToMemoryCache(key, bitmap);
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bitmap;
    }


    /**
     * 下载类 返回输出流
     *
     * @param url
     * @param outputStream
     * @return
     */
    public boolean dowanloadUriToStream(String url, OutputStream outputStream) {
        HttpURLConnection urlConnection = null;
        BufferedOutputStream out = null;
        BufferedInputStream in = null;
        try {
            URL str = new URL(url);
            urlConnection = (HttpURLConnection) str.openConnection();
            in = new BufferedInputStream(urlConnection.getInputStream(), IO_BUFFER_SIZE);
            out = new BufferedOutputStream(outputStream, IO_BUFFER_SIZE);
            int b;
            while ((b = in.read()) != -) {
                out.write(b);
            }
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            urlConnection.disconnect();
            try {
                if (null != out) {
                    out.close();
                }
                if (null != in) {
                    in.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return false;
    }

    private String hashKeyFromUrl(String url) {
        String key = null;
        try {
            MessageDigest digest = MessageDigest.getInstance("MD5");
            digest.update(url.getBytes());
            key = bytesToHexString(digest.digest());
        } catch (Exception e) {
            e.printStackTrace();
        }
        return key;
    }

    private String bytesToHexString(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (int i = ; i < bytes.length; i++) {
            String hex = Integer.toHexString( & bytes[i]);
            if (hex.length() == ) {
                sb.append("0");
            }
            sb.append(hex);
        }
        return sb.toString();
    }

    private Handler mMainHandler = new Handler(Looper.getMainLooper()) {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            LoaderResult result = (LoaderResult) msg.obj;
            ImageView imageView = result.getImageView();
            String url = (String) imageView.getTag();
            if (url.endsWith(result.getUrl())) {
                imageView.setImageBitmap(result.getBitmap());
            }
        }
    };

}
           

这方面在面试的时候问的也好像比较多,所以可以花些时间去了解下。下面是项目代码:

http://download.csdn.net/detail/zhuwentao16/9462142