Android缓存学习入门(二)
本文主要包括以下內容
內存緩存策略
當有一個圖片要去從網絡下載的時候,我們并不會直接去從網絡下載,因為在這個時代,用戶的流量是寶貴的,耗流量的應用是不會得到用戶的青睞的。那我們該怎么辦呢?這樣,我們會先從內存緩存中去查找是否有該圖片,如果沒有就去文件緩存中查找是否有該圖片,如果還沒有,我們就從網絡下載圖片。本博文的側重點是如何做內存緩存,內存緩存的查找策略是:先從強引用緩存中查找,如果沒有再從軟引用緩存中查找,如果在軟引用緩存中找到了,就把它移入強引用緩存;如果強引用緩存滿了,就會根據Lru算法把某些圖片移入軟引用緩存,如果軟引用緩存也滿了,最早的軟引用就會被刪除。這里,我有必要說明下幾個概念:強引用、軟引用、弱引用、Lru。
- 強引用:就是直接引用一個對象,一般的對象引用均是強引用
- 軟引用:引用一個對象,當內存不足并且除了我們的引用之外沒有其他地方引用此對象的情況 下,該對象會被gc回收
- 弱引用:引用一個對象,當除了我們的引用之外沒有其他地方引用此對象的情況下,只要gc被調用,它就會被回收(請注意它和軟引用的區別)
- Lru:Least Recently Used 近期最少使用算法,是一種頁面置換算法,其思想是在緩存的頁面數目固定的情況下,那些最近使用次數最少的頁面將被移出,對于我們的內存緩存來說,強引用緩存大小固定為4M,如果當緩存的圖片大于4M的時候,有些圖片就會被從強引用緩存中刪除,哪些圖片會被刪除呢,就是那些近期使用次數最少的圖片。
注意
In the past, a popular memory cache implementation was a SoftReference or WeakReference bitmap cache, however this is not recommended. Starting from Android 2.3 (API Level 9) the garbage collector is more aggressive with collecting soft/weak references which makes them fairly ineffective. In addition, prior to Android 3.0 (API Level 11), the backing data of a bitmap was stored in native memory which is not released in a predictable manner, potentially causing an application to briefly exceed its memory limits and crash.
在android2.3之后,軟引用與弱引用已經不可靠了,所以慎用。
public class ImageMemoryCache {/*** 從內存讀取數據速度是最快的,為了更大限度使用內存,這里使用了兩層緩存。* 強引用緩存不會輕易被回收,用來保存常用數據,不常用的轉入軟引用緩存。*/private static final String TAG = "ImageMemoryCache";private static LruCache<String, Bitmap> mLruCache; // 強引用緩存private static LinkedHashMap<String, SoftReference<Bitmap>> mSoftCache; // 軟引用緩存private static final int LRU_CACHE_SIZE = 4 * 1024 * 1024; // 強引用緩存容量:4MBprivate static final int SOFT_CACHE_NUM = 20; // 軟引用緩存個數// 在這里分別初始化強引用緩存和弱引用緩存public ImageMemoryCache() {mLruCache = new LruCache<String, Bitmap>(LRU_CACHE_SIZE) {@Override// sizeOf返回為單個hashmap value的大小protected int sizeOf(String key, Bitmap value) {if (value != null)return value.getRowBytes() * value.getHeight();elsereturn 0;}@Overrideprotected void entryRemoved(boolean evicted, String key,Bitmap oldValue, Bitmap newValue) {if (oldValue != null) {// 強引用緩存容量滿的時候,會根據LRU算法把最近沒有被使用的圖片轉入此軟引用緩存Logger.d(TAG, "LruCache is full,move to SoftRefernceCache");mSoftCache.put(key, new SoftReference<Bitmap>(oldValue));}}};mSoftCache = new LinkedHashMap<String, SoftReference<Bitmap>>(SOFT_CACHE_NUM, 0.75f, true) {private static final long serialVersionUID = 1L;/*** 當軟引用數量大于20的時候,最舊的軟引用將會被從鏈式哈希表中移出*/@Overrideprotected boolean removeEldestEntry(Entry<String, SoftReference<Bitmap>> eldest) {if (size() > SOFT_CACHE_NUM) {Logger.d(TAG, "should remove the eldest from SoftReference");return true;}return false;}};}/*** 從緩存中獲取圖片*/public Bitmap getBitmapFromMemory(String url) {Bitmap bitmap;// 先從強引用緩存中獲取synchronized (mLruCache) {bitmap = mLruCache.get(url);if (bitmap != null) {// 如果找到的話,把元素移到LinkedHashMap的最前面,從而保證在LRU算法中是最后被刪除mLruCache.remove(url);mLruCache.put(url, bitmap);Logger.d(TAG, "get bmp from LruCache,url=" + url);return bitmap;}}// 如果強引用緩存中找不到,到軟引用緩存中找,找到后就把它從軟引用中移到強引用緩存中synchronized (mSoftCache) {SoftReference<Bitmap> bitmapReference = mSoftCache.get(url);if (bitmapReference != null) {bitmap = bitmapReference.get();if (bitmap != null) {// 將圖片移回LruCachemLruCache.put(url, bitmap);mSoftCache.remove(url);Logger.d(TAG, "get bmp from SoftReferenceCache, url=" + url);return bitmap;} else {mSoftCache.remove(url);}}}return null;}/*** 添加圖片到緩存*/public void addBitmapToMemory(String url, Bitmap bitmap) {if (bitmap != null) {synchronized (mLruCache) {mLruCache.put(url, bitmap);}}}public void clearCache() {mSoftCache.clear();} }文件緩存策略
當一張圖片從網絡下載成功以后,這個圖片會被加入內存緩存和文件緩存,對于文件緩存來說,這張圖片將被以url的哈希值加cach后綴名的形式存儲在SD卡上,這樣,當下一次再需要同一個url的圖片的時候,就不需要從網絡下載了,而是直接通過url來進行查找。同時一張圖片被訪問時,它的最后修改時間將被更新,這樣的意義在于:當SD卡空間不足的時候,將會按照最后修改時間來刪除40%緩存的圖片,確切來說,那些修改時間比較早的圖片將會被刪除。
public class ImageFileCache {private static final String TAG = "ImageFileCache";//圖片緩存目錄private static final String IMGCACHDIR = "/sdcard/ImgCach";//保存的cache文件寬展名private static final String CACHETAIL = ".cach";private static final int MB = 1024*1024;private static final int CACHE_SIZE = 1;//當SD卡剩余空間小于10M的時候會清理緩存private static final int FREE_SD_SPACE_NEEDED_TO_CACHE = 10;public ImageFileCache() {//清理部分文件緩存removeCache(IMGCACHDIR); }/** * 從緩存中獲取圖片 */public Bitmap getImageFromFile(final String url) { final String path = IMGCACHDIR + "/" + convertUrlToFileName(url);File file = new File(path);if (file != null && file.exists()) {Bitmap bmp = BitmapFactory.decodeFile(path);if (bmp == null) {file.delete();} else {updateFileTime(path);Logger.d(TAG, "get bmp from FileCache,url=" + url);return bmp;}}return null;}/*** 將圖片存入文件緩存 */public void saveBitmapToFile(Bitmap bm, String url) {if (bm == null) {return;}//判斷sdcard上的空間if (FREE_SD_SPACE_NEEDED_TO_CACHE > SdCardFreeSpace()) {//SD空間不足return;}String filename = convertUrlToFileName(url);File dirFile = new File(IMGCACHDIR);if (!dirFile.exists())dirFile.mkdirs();File file = new File(IMGCACHDIR +"/" + filename);try {file.createNewFile();OutputStream outStream = new FileOutputStream(file);bm.compress(Bitmap.CompressFormat.JPEG, 100, outStream);outStream.flush();outStream.close();} catch (FileNotFoundException e) {Logger.d(TAG, "FileNotFoundException");} catch (IOException e) {Logger.d(TAG, "IOException");}} /*** 計算存儲目錄下的文件大小,* 當文件總大小大于規定的CACHE_SIZE或者sdcard剩余空間小于FREE_SD_SPACE_NEEDED_TO_CACHE的規定* 那么刪除40%最近沒有被使用的文件*/private boolean removeCache(String dirPath) {File dir = new File(dirPath);File[] files = dir.listFiles();if (files == null) {return true;}if (!android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){return false;}int dirSize = 0;for (int i = 0; i < files.length; i++) {if (files[i].getName().contains(CACHETAIL)) {dirSize += files[i].length();}}if (dirSize > CACHE_SIZE * MB || FREE_SD_SPACE_NEEDED_TO_CACHE > SdCardFreeSpace()) {int removeFactor = (int) (0.4 * files.length);Arrays.sort(files, new FileLastModifSort());for (int i = 0; i < removeFactor; i++) {if (files[i].getName().contains(CACHETAIL)) {files[i].delete();}}}if (SdCardFreeSpace() <= CACHE_SIZE) {return false;}return true;}/*** 修改文件的最后修改時間*/public void updateFileTime(String path) {File file = new File(path);long newModifiedTime = System.currentTimeMillis();file.setLastModified(newModifiedTime);}/** * 計算SD卡上的剩余空間 */private int SdCardFreeSpace(){StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());double sdFreeMB = ((double)stat.getAvailableBlocks() * (double) stat.getBlockSize()) / MB;return (int) sdFreeMB;} /** * 將url轉成文件名 */private String convertUrlToFileName(String url){return url.hashCode() + CACHETAIL;}/*** 根據文件的最后修改時間進行排序*/private class FileLastModifSort implements Comparator<File> {public int compare(File file0, File file1) {if (file0.lastModified() > file1.lastModified()){return 1;} else if (file0.lastModified() == file1.lastModified()) {return 0;} else {return -1;}}}}References
android中圖片的三級cache策略(內存、文件、網絡)之二:內存緩存策略
android中圖片的三級cache策略(內存、文件、網絡)之三:文件緩存策略
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的Android缓存学习入门(二)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: tensorflow知识点
- 下一篇: 【每日SQL打卡】