一個手寫的圖片三級緩存

這也是一個個人的學習筆記吧,瞭解了一下三級緩存一般是如何實現的。當然寫了這個我也不會用在自己項目中,因爲是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的使用。

如果有什麼不對的地方可以在評論區指出謝謝。

 

 

 

 

 

 

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