標籤:
轉載請註明出處:王亟亟的大牛之路
引用庫的原作者Git:https://github.com/Trinea
現在很多需要動態呈現的View都使用到了H5和WebView,而有些使用的還是傳統的非同步載入操作,今天寫的是傳統的View的實現(H5的可以看這篇文章:http://blog.csdn.net/ddwhan0123/article/details/49683799)
我們常用的諸如ImageLoader ,Picasso 都有類似的效果,今天上的是國內大牛Trinea的ImageCache,那為什麼用他的這個呢?
個人覺得他更輕量級,效果簡單明了,一些衍生和伸展性做的也還不錯。
一.說之前,還是說一下一些比較重要的知識點。(我們不生產知識,我們只是知識的搬運工)
緩衝:java的對象建立需要分配資源較耗費時間,加上建立的對象越多會造成越頻繁的gc影響系統響應。主要使用單例模式、緩衝(圖片緩衝、線程池、View緩衝、IO緩衝、訊息緩衝、通知欄notification緩衝)及其他方式減少對象建立。
然後緩衝中,又圖片緩衝、線程池、View緩衝、IO緩衝、訊息緩衝、通知欄notification緩衝等。(這不是我們主要講的內容,不知道可以自己Google,這裡只是例舉下)
那麼,在實用情境下會有哪些常見的非同步載入問題呢?
a. 行item圖片顯示重複
這個顯示重複是指當前行item顯示了之前某行item的圖片。
比如ListView滑動到第2行會非同步載入某個圖片,但是載入很慢,載入過程中listView已經滑動到了第14行,且滑動過程中該圖片載入結束,第2行已不在螢幕內,根據上面介紹的緩衝原理,第2行的view可能被第14行複用,這樣我們看到的就是第14行顯示了本該屬於第2行的圖片,造成顯示重複。
b. 行item圖片顯示錯亂
這個顯示錯亂是指某行item顯示了不屬於該行item的圖片。
比如ListView滑動到第2行會非同步載入某個圖片,但是載入很慢,載入過程中listView已經滑動到了第14行,第2行已不在螢幕內,根據上面介紹的緩衝原理,第2行的view可能被第14行複用,第14行顯示了第2行的View,這時之前的圖片載入結束,就會顯示在第14行,造成錯亂。
c. 行item圖片顯示閃爍
上面b的情況,第14行圖片又很快載入結束,所以我們看到第14行先顯示了第2行的圖片,立馬又顯示了自己的圖片進行覆蓋造成閃爍錯亂
因為,如果我們用 Back鍵回到上一個Activity再進來的時候是不是還要重新載入?或者我第一次載入存到本地,第二次再讀本地IO?所以,緩衝成為提高使用者體驗和節約效能一個很好的途徑。
二.ImageCache
ImageCache有什麼優勢呢?
(1). 使用簡單 (2). 輕鬆擷取及預取新圖片 (3). 包含二級緩衝 (4). 可選擇多種緩衝演算法(FIFO、LIFO、LRU、MRU、LFU、MFU等13種)或自訂緩衝演算法 (5). 可方便的儲存及初始化恢複資料 (6). 支援檔案sd卡儲存及自訂檔案名稱規則 (7). 省流量效能佳(有且僅有一個線程擷取圖片) (8). 支援不同類型網路處理 (9). 可根據系統配置初始化緩衝 (10). 擴充性強 (11). 支援等待隊列 (12). 包含map的大多數介面。
-1.使用:
a.初始化一個ImageCache 對象
public static final ImageCache IMAGE_CACHE = CacheManager.getImageCache();
b.需要載入圖片的地方調用get(String imageUrl, View view)非同步載入圖片
IMAGE_CACHE.get(imageUrl, imageView);
就是辣麼簡單,效果就能達到。
當然,對緩衝,線程等等等一系列有要求的,也可以加以設定:
/** image cache **/public static final ImageCache IMAGE_CACHE = new ImageCache();static { OnImageCallbackListener imageCallBack = new OnImageCallbackListener() { private static final long serialVersionUID = 1L; // callback function before get image, run on ui thread @Override public void onPreGet(String imageUrl, View view) { // Log.e(TAG_CACHE, "pre get image"); } // callback function after get image successfully, run on ui thread @Override public void onGetSuccess(String imageUrl, Bitmap loadedImage, View view, boolean isInCache) { // can be another view child, like textView and so on if (view != null && loadedImage != null && view instanceof ImageView) { ImageView imageView = (ImageView)view; imageView.setImageBitmap(loadedImage); } } // callback function after get image failed, run on ui thread @Override public void onGetFailed(String imageUrl, Bitmap loadedImage, View view, FailedReason failedReason) { Log.e(TAG_CACHE, new StringBuilder(128).append("get image ").append(imageUrl).append(" error") .toString()); } @Override public void onGetNotInCache(String imageUrl, View view) { } }; IMAGE_CACHE.setOnImageCallbackListener(imageCallBack);}
-2.功能
(1) 多種建構函式,可根據系統配置初始化緩衝
public ImageCache()
public ImageCache(int primaryCacheMaxSize)
public ImageCache(int primaryCacheMaxSize, int secondaryCacheMaxSize)
public ImageCache(int primaryCacheMaxSize, int primaryCacheThreadPoolSize, int secondaryCacheMaxSize, int secondaryCacheThreadPoolSize)
支援四種建構函式,支援一級和二級緩衝大小及擷取圖片線程池大小的設定。預設會根據系統可用記憶體大小設定緩衝大小,根據系統Cpu個數設定線程池大小。
(2)、擷取圖片及自動預取
get(String imageUrl, View view)非同步擷取圖片,在圖片擷取成功後自動調用OnImageCallbackListener的onGetSuccess函數,返回是否已在緩衝中
get(String imageUrl, List urlList, View view)非同步擷取圖片,在圖片擷取成功後自動調用OnImageCallbackListener的onGetSuccess函數,並且根據imageUrl在urlList中的位置向前向後預取圖片,返回是否已在緩衝中。
public void setForwardCacheNumber(int forwardCacheNumber) 向前預取圖片個數設定,預設為PreloadDataCache#DEFAULT_FORWARD_CACHE_NUMBER
public void setBackwardCacheNumber(int backwardCacheNumber)向後預取圖片個數設定預設,預設為PreloadDataCache#DEFAULT_BACKWARD_CACHE_NUMBER
public CacheObject get(K key)
public CacheObject get(K key, List keyList)
兩個介面是直接同步處理擷取圖片,且擷取成功後不會調用OnImageCallbackListener的onGetSuccess函數
(3)、設定緩衝演算法
setCacheFullRemoveType(CacheFullRemoveType cacheFullRemoveType)
設定緩衝演算法,緩衝演算法即為緩衝滿時為了插入新資料,刪除舊資料的規則。
目前包括FIFO、LIFO、LRU、MRU、LFU、MFU、優先順序低先刪除、優先順序高先刪除、資料小先刪除、資料大先刪除、圖片小先刪除、圖片大先刪除、永不刪除。還可以通過實現CacheFullRemoveType來自訂緩衝演算法。。預設為RemoveTypeUsedCountSmall,即LRU使用頻率低先刪除。下面詳細介紹各個演算法:
RemoveTypeEnterTimeFirst FIFO先進先出,先進入先刪除
RemoveTypeEnterTimeLast LIFO後進先出,後進入先刪除
RemoveTypeLastUsedTimeFirst LRU(Least Recently User),最先使用先刪除
RemoveTypeLastUsedTimeLast MRU(Most Recently Used),最近使用先刪除
RemoveTypeUsedCountSmall LFU(Least Frequently Used),使用頻率低先刪除
RemoveTypeUsedCountBig MRU(Most Frequently Used),使用頻率高先刪除
RemoveTypePriorityLow 優先順序低先刪除
RemoveTypePriorityHigh 優先順序低先刪除
RemoveTypeBitmapSmall 圖片小的先刪除
RemoveTypeBitmapLarge 圖片大的先刪除
RemoveTypeDataBig 資料大先刪除,根據快取資料的compareTo函數決定
RemoveTypeDataSmall 資料小先刪除,根據快取資料的compareTo函數決定
RemoveTypeNotRemove 不刪除,緩衝滿時不再允許插入新資料
自訂緩衝演算法只需要實現CacheFullRemoveType的compare方法即可。比較結果小於0表示會被先刪除
public class RemoveTypePriorityHigh<T> implements CacheFullRemoveType<T> { private static final long serialVersionUID = 1L; @Override public int compare(CacheObject<T> obj1, CacheObject<T> obj2) { return (obj2.getPriority() > obj1.getPriority()) ? 1 : ((obj2.getPriority() == obj1.getPriority()) ? 0 : -1); }}
(4)、儲存及初始化恢複資料
public boolean saveDataToDb(Context context, String tag)
儲存資料到資料庫,可在程式退出時調用,不建議在每個activity onDestrory時調用,而是整個程式退出(比如”退出確認對話方塊”點擊確認)時,見本文3.1常見問題集。
public void initData(Context context, String tag)
初始化恢複資料,可在程式剛開始載入時調用,不建議在每個activity oncreate調用,而是整個程式初始化(比如Application的onCreate函數)時,見本文3.1常見問題集。
(5)、是否啟用隊列
setOpenWaitingQueue(boolean isOpenWaitingQueue)
當不同view通過get函數擷取圖片時,是否開啟等待隊列。
若開啟,儲存所有view,圖片擷取成功後依次調用OnImageCallbackListener的onGetSuccess函數;否則僅儲存最後調用get的view,圖片擷取成功後調用OnImageCallbackListener的onGetSuccess函數
預設開啟隊列等待。如果希望最優效能且情境滿足,可設定為false。
(6)、設定圖片擷取方式介面
setOnGetDataListener(OnGetDataListener
setOnGetDataListener(ImageCacheManager.getImageFromSdcardListener());
注意這種方式compressListener是無效的,如果希望利用compressListener,可以如下設定:
setOnGetDataListener(new OnGetDataListener<String, Bitmap>() { private static final long serialVersionUID = 1L; @Override public CacheObject<Bitmap> onGetData(String imagePath) { if (!FileUtils.isFileExist(imagePath)) { return null; } CompressListener compressListener = IMAGE_CACHE.getCompressListener(); int compressSize = 0; if (compressListener != null) { compressSize = compressListener.getCompressSize(imagePath); } Bitmap bm; if (compressSize > 1) { BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = compressSize; bm = BitmapFactory.decodeFile(imagePath, option); } else { bm = BitmapFactory.decodeFile(imagePath); } return (bm == null ? null : new CacheObject<Bitmap>(bm)); }});
5、記憶體溢出OOM問題?
通過setCompressListener介面壓縮圖片,getCompressSize傳回值為圖片長寬縮放比例,同BitmapFactory.Options#inSampleSize
2345678910111213141516IMAGE_CACHE.setCompressListener(new CompressListener() { @Override public int getCompressSize(String imagePath) { if (FileUtils.isFileExist(imagePath)) { long fileSize = FileUtils.getFileSize(imagePath) / 1000; /** * if image bigger than 100k, compress to 1/(n + 1) width and 1/(n + 1) height, n is fileSize / 100k **/ if (fileSize > 100) { return (int)(fileSize / 100) + 1; } } return 1; }});
還可見 圖片OutOfMemory異常bitmap size exceeds VM budget的原因及解決方案
更多內容可以在作者的文檔中找到。
接下來貼一下我們的範例程式碼:
包目錄:
源碼地址:https://github.com/ddwhan0123/BlogSample/tree/master/ImageCacheDemo
感謝觀眾老爺點個贊,謝謝
Android第三方資源使用之ImageCache