在Android中,有一个叫做LruCache类专门用来做图片缓存处理的。
它有一个特点,当缓存的图片达到了预先设定的值的时候,那么近期使用次数最少的图片就会被回收掉。
我们来看看它的源码(注意是在android.support.v4.util下):

package android.support.v4.util;import java.util.LinkedHashMap;import java.util.Map;public class LruCache<K, V> {    //内部维护了一个LinkedHashMap,为什么要用这个,后面介绍    private final LinkedHashMap<K, V> map;    private int size; //已缓存的大小    private int maxSize; //缓存最大值    private int putCount; //往集合里put的次数    private int createCount; //创建的个数    private int evictionCount;//移除的个数    private int hitCount;//命中次数    private int missCount;//丢失的次数    /** * 构造方法 * @param maxSize 缓存的最大值 */    public LruCache(int maxSize) {        if (maxSize <= 0) {            throw new IllegalArgumentException("maxSize <= 0");        }        this.maxSize = maxSize;        this.map = new LinkedHashMap<K, V>(0, 0.75f, true);    }    /** * 重新设置最大缓存大小 * * @param maxSize */    public void resize(int maxSize) {        if (maxSize <= 0) {            throw new IllegalArgumentException("maxSize <= 0");        }        synchronized (this) {            this.maxSize = maxSize;        }        trimToSize(maxSize);    }    /** * 通过key获取相应的value,或者创建返回相应的value。相应的value会移动到队列的头部, * 如果item的value没有被cache或者不能被创建,则返回null。 */    public final V get(K key) {        if (key == null) {            throw new NullPointerException("key == null");        }        V mapValue;        synchronized (this) {            mapValue = map.get(key);            if (mapValue != null) {                hitCount++;                return mapValue;            }            missCount++;        }        //如果丢失了就试图创建一个value        V createdValue = create(key);        if (createdValue == null) {            return null;        }        //同步,保证多线程下访问安全        synchronized (this) {            createCount++;            mapValue = map.put(key, createdValue);            if (mapValue != null) {                // 如果前面存在一个旧的value,那么撤销put()                 map.put(key, mapValue);            } else {                size += safeSizeOf(key, createdValue);            }        }        if (mapValue != null) {            entryRemoved(false, key, createdValue, mapValue);            return mapValue;        } else {            trimToSize(maxSize);            return createdValue;        }    }    /** * 在缓存集合(LinkedHashMap)队首添加value * * @return the previous value mapped by {@code key}. */    public final V put(K key, V value) {        if (key == null || value == null) {            throw new NullPointerException("key == null || value == null");        }        V previous;        synchronized (this) {            putCount++;            size += safeSizeOf(key, value);            previous = map.put(key, value);            if (previous != null) {                 //返回的先前的value                size -= safeSizeOf(key, previous);            }        }        if (previous != null) {            entryRemoved(false, key, previous, value);        }        trimToSize(maxSize);        return previous;    }    /** * 调整cache空间 * 当缓存的大小超过原来设定的maxSize时,遍历map,将多余的项(代码中对应toEvict)剔除掉,直到当 * 前cache的大小等于或小于限定的大小 * @param maxSize the maximum size of the cache before returning. May be -1 * to evict even 0-sized elements. */    public void trimToSize(int maxSize) {        while (true) {            K key;            V value;            synchronized (this) {                if (size < 0 || (map.isEmpty() && size != 0)) {                    throw new IllegalStateException(getClass().getName()                            + ".sizeOf() is reporting inconsistent results!");                }                if (size <= maxSize || map.isEmpty()) {                    break;                }                Map.Entry<K, V> toEvict = map.entrySet().iterator().next();                key = toEvict.getKey();                value = toEvict.getValue();                map.remove(key);                size -= safeSizeOf(key, value);                evictionCount++;            }            entryRemoved(true, key, value, null);        }    }    /** * 从缓存中移除掉key对应的value * * @return the previous value mapped by {@code key}. */    public final V remove(K key) {        if (key == null) {            throw new NullPointerException("key == null");        }        V previous;        synchronized (this) {            previous = map.remove(key);            if (previous != null) {                size -= safeSizeOf(key, previous);            }        }        if (previous != null) {            entryRemoved(false, key, previous, null);        }        return previous;    }    /** * 当item被回收或者删掉时调用。该方法当value被回收释放存储空间时被remove调用, * 或者替换item值时put调用,默认实现什么都没做。 * * @param evicted true if the entry is being removed to make space, false * if the removal was caused by a {@link #put} or {@link #remove}. * @param newValue the new value for {@code key}, if it exists. If non-null, * this removal was caused by a {@link #put}. Otherwise it was caused by * an eviction or a {@link #remove}. */    protected void entryRemoved(boolean evicted, K key, V oldValue, V newValue) {}    /** * 当某Item丢失时会调用到,返回计算的相应的value或者null * <p>If a value for {@code key} exists in the cache when this method * returns, the created value will be released with {@link #entryRemoved} * and discarded. This can occur when multiple threads request the same key * at the same time (causing multiple values to be created), or when one * thread calls {@link #put} while another is creating a value for the same * key. */    protected V create(K key) {        return null;    }    /** * 计算某个item的大小,如果是Bitmap,这应该是bitmap.getByteCount(); */    private int safeSizeOf(K key, V value) {        int result = sizeOf(key, value);        if (result < 0) {            throw new IllegalStateException("Negative size: " + key + "=" + value);        }        return result;    }    protected int sizeOf(K key, V value) {        return 1;    }    /** * 清空缓存 */    public final void evictAll() {        trimToSize(-1); // -1 will evict 0-sized elements    }    /** * For caches that do not override {@link #sizeOf}, this returns the number * of entries in the cache. For all other caches, this returns the sum of * the sizes of the entries in this cache. */    public synchronized final int size() {        return size;    }    /** * For caches that do not override {@link #sizeOf}, this returns the maximum * number of entries in the cache. For all other caches, this returns the * maximum sum of the sizes of the entries in this cache. */    public synchronized final int maxSize() {        return maxSize;    }    /** * Returns the number of times {@link #get} returned a value that was * already present in the cache. */    public synchronized final int hitCount() {        return hitCount;    }    /** * 返回get()为null的次数或者创建的次数 */    public synchronized final int missCount() {        return missCount;    }    /** * 返回创建create()的次数 */    public synchronized final int createCount() {        return createCount;    }    /** * 返回put的次数 */    public synchronized final int putCount() {        return putCount;    }    /** * 返回被回收的数量 */    public synchronized final int evictionCount() {        return evictionCount;    }    /** * 返回当前cache的副本,从最近最少访问到最多访问 */    public synchronized final Map<K, V> snapshot() {        return new LinkedHashMap<K, V>(map);    }    @Override public synchronized final String toString() {        int accesses = hitCount + missCount;        int hitPercent = accesses != 0 ? (100 * hitCount / accesses) : 0;        return String.format("LruCache[maxSize=%d,hits=%d,misses=%d,hitRate=%d%%]",                maxSize, hitCount, missCount, hitPercent);    }}

我们来分析下LinkedHashMap:
LinkedHashMap中的get()方法不仅返回所匹配的值,并且在返回前还会将所匹配的key对应的entry调整在列表中的顺序(LinkedHashMap使用双链表来保存数据),让它处于链表的最后。当然,这种情况必须是在LinkedHashMap中accessOrder==true的情况下才生效的,反之就是get()方法不会改变被匹配的key对应的entry在列表中的位置,这样将大大方便了缓存顺序的调整和缓存的移除。这就是为什么要使用LinkedHashMap的原因。

        @Override         public V get(Object key) {        /* * This method is overridden to eliminate the need for a polymorphic * invocation in superclass at the expense of code duplication. */        if (key == null) {            HashMapEntry<K, V> e = entryForNullKey;            if (e == null)                return null;            if (accessOrder)                makeTail((LinkedEntry<K, V>) e);            return e.value;        }        int hash = Collections.secondaryHash(key);        HashMapEntry<K, V>[] tab = table;        for (HashMapEntry<K, V> e = tab[hash & (tab.length - 1)];                e != null; e = e.next) {            K eKey = e.key;            if (eKey == key || (e.hash == hash && key.equals(eKey))) {                if (accessOrder)                     makeTail((LinkedEntry<K, V>) e);//调整顺序                return e.value;            }        }        return null;    }

好了,LruCache就介绍到这里,下面有个使用例子,大家可以看看:

package com.king.imagechooser.util;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.os.Handler;import android.os.Looper;import android.os.Message;import android.util.DisplayMetrics;import android.util.Log;import android.util.LruCache;import android.view.ViewGroup.LayoutParams;import android.widget.ImageView;import java.lang.reflect.Field;import java.util.LinkedList;import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import java.util.concurrent.Semaphore;/** * 本地图片加载框架(读取本地图片) * Created by King on 2016/3/21. */public class ImageLoader {    /** * 图片缓存 */    private LruCache<String, Bitmap> mCaches;    /** * 线程池 */    private ExecutorService mThreadPool;    /** * 默认线程数量 */    private final static int DEFAULT_THREAD_COUNTS = 1;    /** * 信号量 */    private Semaphore mSemaphore = new Semaphore(0);    private Semaphore mSemaphoreThreadPool;    private Type mType = Type.LIFO;    public enum Type {        FIFO, LIFO    }    private LinkedList<Runnable> mTaskQueue;    private Thread mPoolThread;    private Handler mPoolThreadHandler;    private Handler mUIHandler;    /** * 单例 */    private static ImageLoader mInstance;    private ImageLoader(int treadCount, Type type) {        init(treadCount, type);    }    public static ImageLoader getInstance(int treadCount, Type type) {        if (mInstance == null) {            synchronized (ImageLoader.class) {                if (mInstance == null) {                    mInstance = new ImageLoader(treadCount, type);                }            }        }        return mInstance;    }    /** * 初始化 * * @param threadCount * @param type */    public void init(int threadCount, Type type) {        mPoolThread = new Thread() {            @Override            public void run() {                Looper.prepare();                mPoolThreadHandler = new Handler() {                    @Override                    public void handleMessage(Message msg) {                        //从线程池取一个任务去执行                        mThreadPool.execute(getTaskThread());                        try {                            //最多执行threadCount个线程,利用信号量来控制                            mSemaphoreThreadPool.acquire();                        } catch (Exception e) {                            Log.e("King", e.getMessage());                        }                    }                };                //释放信号量                mSemaphore.release();                Looper.loop();            }        };        mPoolThread.start();        //获取最大可用内存        int maxMemory = (int) Runtime.getRuntime().maxMemory();        int cacheMemory = maxMemory / 4;        //初始化LruCache        mCaches = new LruCache<String, Bitmap>(cacheMemory) {            @Override            protected int sizeOf(String key, Bitmap value) {                return value.getRowBytes() * value.getHeight();            }        };        //创建线程池        mThreadPool = Executors.newFixedThreadPool(threadCount);        mTaskQueue = new LinkedList<Runnable>();        mType = type;        mSemaphoreThreadPool = new Semaphore(threadCount);    }    /** * 显示图片到ImageView */    public void loadImage(final String path, final ImageView imageView) {        imageView.setTag(path);        if (mUIHandler == null) {            mUIHandler = new Handler() {                @Override                public void handleMessage(Message msg) {                    //获取图片,为ImageView回调设置图片                    ImageHolder holder = (ImageHolder) msg.obj;                    Bitmap bm = holder.bitmap;                    ImageView iv = holder.imageView;                    String path = holder.path;                    if (iv.getTag().toString().equals(path)) {                        iv.setImageBitmap(bm);                    }                }            };        }        Bitmap bitmap = getBitmapFromCache(path);        if (bitmap != null) {            refreshBitmap(path, imageView, bitmap);        } else {            addTaskThread(new Runnable() {                @Override                public void run() {                    //获取imageView大小                    ImageSize imageSize = getImageViewSize(imageView);                    //压缩图片                    Bitmap bitmap = decodeBitmapFromPath(path, imageSize.width, imageSize.height);                    //将图片加入缓存                    addBitmapToCache(path, bitmap);                    //发送消息,更新视图                    refreshBitmap(path, imageView, bitmap);                    //释放信号量,保证剩余线程继续执行                    mSemaphoreThreadPool.release();                }            });        }    }    private void refreshBitmap(String path, ImageView imageView, Bitmap bitmap) {        ImageHolder holder = new ImageHolder();        holder.bitmap = bitmap;        holder.imageView = imageView;        holder.path = path;        Message msg = Message.obtain();        msg.obj = holder;        mUIHandler.sendMessage(msg);    }    /** * 获取ImageView的实际大小 */    protected ImageSize getImageViewSize(ImageView imageView) {        ImageSize size = new ImageSize();        DisplayMetrics displayMetrics = imageView.getContext().getResources().getDisplayMetrics();        LayoutParams lp = imageView.getLayoutParams();        int width = imageView.getWidth();        if (width <= 0) {            width = lp.width; //获取ImageView在layout中声明的宽度        }        if (width <= 0) {            width = getImageViewFieldValue(imageView, "mMaxWidth");//获取ImageView在layout中的最大宽度        }        if (width <= 0) {            width = displayMetrics.widthPixels;        }        int height = imageView.getHeight();        if (height <= 0) {            height = lp.height; //获取ImageView在layout中声明的高度        }        if (height <= 0) {            height = getImageViewFieldValue(imageView, "mMaxHeight"); //获取ImageView在layout中的最大高度        }        if (height <= 0) {            height = displayMetrics.heightPixels;        }        size.width = width;        size.height = height;        return size;    }    //通过反射获取ImageView的属性(为了兼容低版本Api)    private static int getImageViewFieldValue(Object object, String fieldName) {        int value = 0;        try {            Field field = ImageView.class.getDeclaredField(fieldName);            field.setAccessible(true);            int fieldValue = field.getInt(object);            if (fieldValue > 0 && fieldValue < Integer.MAX_VALUE) {                value = fieldValue;            }        } catch (NoSuchFieldException e) {            Log.e("King", e.getMessage());        } catch (IllegalAccessException e) {            e.printStackTrace();        }        return value;    }    /** * 压缩图片 * * @param path * @param width * @param height * @return */    protected Bitmap decodeBitmapFromPath(String path, int width, int height) {        try {            BitmapFactory.Options options = new BitmapFactory.Options();            options.inJustDecodeBounds = true;            BitmapFactory.decodeFile(path, options);            options.inSampleSize = calculateInSampleSize(options, width, height);            options.inJustDecodeBounds = false;            return BitmapFactory.decodeFile(path, options);        } catch (Throwable tr) {            Log.e("King", tr.getMessage());        }        return null;    }    // 计算图片的缩放值    public static int calculateInSampleSize(BitmapFactory.Options options,                                            int reqWidth, int reqHeight) {        try {            final int height = options.outHeight;            final int width = options.outWidth;            int inSampleSize = 1;            if (height > reqHeight || width > reqWidth) {                final int heightRatio = Math.round((float) height                        / (float) reqHeight);                final int widthRatio = Math.round((float) width                        / (float) reqWidth);                inSampleSize = Math.max(heightRatio, widthRatio);            }            return inSampleSize;        } catch (Throwable e) {            Log.e("King", e.getMessage());        }        return 1;    }    /** * 将图片添加到缓存 * * @param path * @param bm */    private void addBitmapToCache(String path, Bitmap bm) {        if (getBitmapFromCache(path) == null) {            if (bm != null) {                mCaches.put(path, bm);            }        }    }    //添加线程到集合中,方便管理    private synchronized void addTaskThread(Runnable runnable) {        try {            mTaskQueue.add(runnable);            if (mPoolThreadHandler == null) {                //请求信号量,保证Handler为空时再请求,从而避免重复请求                mSemaphore.acquire();            }            mPoolThreadHandler.sendEmptyMessage(0x110);        } catch (Throwable tr) {            Log.e("King", tr.getMessage(), tr);        }    }    /** * 获取线程 * * @return */    private Runnable getTaskThread() {        if (mType == Type.FIFO) {            return mTaskQueue.removeFirst();        } else if (mType == Type.LIFO) {            return mTaskQueue.removeLast();        }        return null;    }    /** * 从缓存中获取图片 */    public Bitmap getBitmapFromCache(String key) {        return mCaches.get(key);    }    private class ImageHolder {        Bitmap bitmap;        ImageView imageView;        String path;    }    private class ImageSize {        int width;        int height;    }}

更多相关文章

  1. Android(安卓)Material Design 组件集合Demo,附源码地址 :CimoGall
  2. thread in android ndk
  3. Android(安卓)为apk文件签名,增加修改系统时间等权限
  4. android背景图片更换――经典例子
  5. 百度地图开发 android App 数字签名(SHA1)获取办法
  6. 简单基站定位程序
  7. Android(安卓)thumbnail 缩略图的获取及与原始图片的映射关系
  8. TextView通过获得dimens.xml中定义的大小来动态设置字体大小
  9. Android开发本地及网络Mp3音乐播放器(五)实现专辑封面图片

随机推荐

  1. android之【RelativeLayout布局】
  2. 面向UDP的Android——PC双向通信(三):在Andr
  3. [转] Android电源管理
  4. android:configChanges
  5. Android是什么 之三-------手机之硬件形
  6. android kernel 初始化 1
  7. Android 6.0棉花糖新特性,
  8. android TextView 走马灯效果
  9. Android api,Android SDK
  10. android后台进程隐藏手段