一个手写的图片三级缓存

这也是一个个人的学习笔记吧,了解了一下三级缓存一般是如何实现的。当然写了这个我也不会用在自己项目中,因为是Glide它不香了还是Fresco不好用??前面说下需要了解的一些知识,后面全是代码。

首先,图片编码:

  1. ALPHA_8 图片只有alpha值,没有RGB值,一1个像素占用一个字节
  2. ARGB_4444 alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占4个bites共16bites,2个字节
  3. ARGB_8888 alpha(A)值,Red(R)值,Green(G)值,Blue(B)值各占8个bites,共32bites,4个字节。
  4. RGB_565 没有alpha(A)值,不支持透明和半透明,Red(R)值占5个bites ,Green(G)值占6个bites ,Blue(B)值占5个bites,共16bites,2个字节。

基本上常用的就这些。系统默认的是ARGB_8888也就是默认一个像素点4字节,不需要透明度的时候可以手动切成RGB_565。

LruCache原理

简而言之就是内部维护了一个双向链表,LinkedHashMap。会先设定一个缓存大小,一般为可用内存的八分之一。如果有新数据插入,会把新数据插到队尾,所以头部的数据就是最近最少使用的数据。插入之后如果内存大小超过限制了,那就会移除头部元素。

InBitmap

需要事先设置Inmutable为true。Inmutable是表示这个bitmap的内存大小可变。我们用Bitmap工厂创建一个Bitmap后,都会从内存获得一块空间。InBitmap就是让后面的Bitmap直接复用之前的Bitmap空间,所以这个内存空间大小必须是可以变动的才行。

但是使用有很多限制,官网上有直接给出代码,抄就行了。

  • 4.4之前的版本inBitmap只能够重用相同大小的Bitmap内存区域。简单而言,被重用的Bitmap需要与新的Bitmap规格完全一致,否则不能重用。
  • 4.4之后的版本系统不再限制旧Bitmap与新Bitmap的大小,保证旧Bitmap的大小是大于等于新Bitmap大小即可。
  • 旧Bitmap必须是mutable的

三级缓存

  • 首次加载的时候通过网络加载,获取图片,然后保存到内存和 SD 卡中。
  • 运行 APP 时,优先访问内存中的图片缓存。
  • 如果内存没有,则加载本地 SD 卡中的图片。

内存->磁盘->网络。加载速度和顺序就是这样。

磁盘缓存

DiskLruCache,直接在GitHub上可以下载到,也算是google官方推荐的。剩下的直接上代码了。


/**
 */
public class ImageManager {

    //磁盘缓存
    private DiskLruCache diskLruCache;

    private LruCache<String, Bitmap> lruCache;

    //复用池
    private Set<SoftReference<Bitmap>> reusableBitmaps;
    //引用队列
    private ReferenceQueue<Bitmap> referenceQueue;
    //取出线程
    private Thread clearThread;

    private static volatile ImageManager imageManager;

    public static ImageManager getInstance() {
        if (imageManager == null) {
            synchronized (ImageManager.class) {
                if (imageManager == null) {
                    imageManager = new ImageManager();
                }
            }
        }
        return imageManager;
    }


    public void init(Context context) {
        ActivityManager am = (ActivityManager) context.getApplicationContext().getSystemService(Context.ACTIVITY_SERVICE);
        lruCache = new LruCache<String, Bitmap>(am.getMemoryClass() / 8 * 1024 * 1024) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount();
            }

            @Override
            protected void entryRemoved(boolean evicted, String key, Bitmap oldValue, Bitmap newValue) {
                if (oldValue.isMutable()) {
                    reusableBitmaps.add(new SoftReference<Bitmap>(oldValue, referenceQueue));
                } else {
                    oldValue.recycle();
                }
            }
        };
        reusableBitmaps =
                Collections.synchronizedSet(new HashSet<SoftReference<Bitmap>>());
        clearThread = new Thread(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    try {
                        Reference remove = referenceQueue.remove();
                        Bitmap bitmap = (Bitmap) remove.get();
                        if (bitmap != null) {
                            bitmap.recycle();
                        }
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        clearThread.start();
        referenceQueue = new ReferenceQueue();
        File file = getDiskCacheDir(context, "thumb");
        try {
            diskLruCache = DiskLruCache.open(file, BuildConfig.VERSION_CODE, 1,
                    10 * 1024 * 1024);
        } catch (IOException e) {

        }
    }


    /**
     * 写进内存
     *
     * @param key
     * @param bitmap
     */
    public void putToMemory(String key, Bitmap bitmap) {
        lruCache.put(key, bitmap);
    }

    /**
     * 从复用池获取
     */
    public Bitmap getFromPool() {
        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.HONEYCOMB) {
            return null;
        }
        Bitmap getInBitmap = null;
        Iterator<SoftReference<Bitmap>> iterator = reusableBitmaps.iterator();
        while (iterator.hasNext()) {
            SoftReference<Bitmap> next = iterator.next();
            Bitmap bitmap = next.get();
            if (bitmap != null) {
                getInBitmap = bitmap;
                break;
            }
        }
        return getInBitmap;
    }


    /**
     * 从内存获取
     *
     * @param key
     * @return
     */
    public Bitmap getFromMemory(String key) {
        return lruCache.get(key);
    }

    /**
     * 写入磁盘的方法
     *
     * @param key
     * @param bitmap
     */
    public void putToDisk(String key, Bitmap bitmap) {
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = diskLruCache.get(key);
            //如果磁盘没有文件,写入
            if (snapshot == null) {
                DiskLruCache.Editor edit = diskLruCache.edit(key);
                OutputStream outputStream = edit.newOutputStream(0);
                bitmap.compress(Bitmap.CompressFormat.JPEG, 0, outputStream);
                edit.commit();
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (snapshot != null) {
                snapshot.close();
            }
        }
    }

    BitmapFactory.Options options = new BitmapFactory.Options();

    public Bitmap getFromDisk(String key, Bitmap bitmap) {
        Bitmap diskBitmap = null;
        DiskLruCache.Snapshot snapshot = null;
        try {
            snapshot = diskLruCache.get(key);
            if (snapshot == null) {
                return null;
            }
            InputStream inputStream = snapshot.getInputStream(0);
            options.inMutable = true;
            options.inBitmap = bitmap;
            diskBitmap=BitmapFactory.decodeStream(inputStream, null, options);
            putToMemory(key, diskBitmap);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (snapshot != null) {
                snapshot.close();
            }
        }
        return diskBitmap;
    }

    public void clearAll() {
        lruCache.evictAll();
    }

    public File getDiskCacheDir(Context context, String uniqueName) {

        String cachePath;

        if (Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {

            cachePath = context.getExternalCacheDir().getPath();
        } else {

            cachePath = context.getCacheDir().getPath();
        }

        return new File(cachePath + File.separator + uniqueName);
    }
}

这就是三级缓存的管理类,从内存到磁盘。要注意的地方不多,大部分地方也加上了注释。缺陷点的地方就是InBitmap的使用。

如果有什么不对的地方可以在评论区指出谢谢。

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章