在Android開發中,網絡IO等耗時操作應獨立于UI線程,對于需要使用大量網絡圖檔的應用來說,能夠異步下載下傳并緩存圖檔的子產品顯得非常重要。
如名,異步緩存系統的兩大特性是:
1.異步下載下傳資源
2.緩存系統
基本思路:要實作一個最簡單的異步緩存功能,可以開啟一個Thread,通過Http下載下傳圖檔,用HashMap緩存圖檔即可。
建立緩存時可以使用記憶體緩存和磁盤緩存,擷取資料時先從記憶體緩存讀取,如果沒有資料則從磁盤緩存讀取,再沒有則從網絡下載下傳,下載下傳完後記得寫入磁盤緩存,記憶體緩存。
本文要介紹的 Universal-Image-Loader (以下簡稱UIL)也是類似思路,但功能更加強大,其建立了一套架構,友善擴充和配置,使用起來更為靈活。
UIL是GitHub上的開源項目,位址:https://github.com/nostra13/Android-Universal-Image-Loader
這裡提供了完整的源碼和示例,初次使用請閱讀ReadMe文檔了解基本情況。
使用方法:
1.Android工程導入庫檔案 universal-image-loader-xxx-with-sources.jar(在下載下傳的downloads目錄)
2.設定權限
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
<!-- 如果要緩存圖檔到磁盤需要設定 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
<application android:name="MyApplication">
...
</application>
</manifest>
3.編寫代碼
隻需要三個步驟:
(1) 配置選項
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(getApplicationContext())
...
.build();
(2)初始化ImageLoader
ImageLoader.getInstance().init(config);
(3)顯示圖檔
ImageLoader.getInstance().displayImage(imageUrl, imageView);
或
ImageLoader.getInstance().displayImage(imageUrl, listener); //通過listener傳遞ImageView對象
其中1,2兩步初始化過程可以在繼承Application的類中完成,第3步可以在Activity或Adapter等需要顯示圖檔的類中完成
ImageLoader實作了displayImage和loadImage的多個不同版本的方法,具體可參考源碼。
支援的URI類型
String imageUri = "http://site.com/image.png"; // 網絡資源
String imageUri = "file:///mnt/sdcard/image.png"; // SD card 資源
String imageUri = "content://media/external/audio/albumart/13"; // content provider 資源
String imageUri = "assets://image.png"; // assets 資源
String imageUri = "drawable://" + R.drawable.image; // drawables 資源(針對非.9.png圖檔)
注:為提高效率,當使用"drawable://格式uri"時,盡量使用本地方法ImageView.setImageResource(...)
配置選項:
該項目包含2個配置選項的類:
1.ImageLoaderConfiguration 是全局配置,建立ImageLoader時使用
2.DisplayImageOptions 是局部配置,每次加載或顯示圖檔時都可指定顯示選項
ImageLoaderConfiguration 示例
//這裡隻是列出各種情況的例子,在設定自己的選項時不要完全拷貝這裡的代碼
//ImageLoaderConfiguration類中有個方法會判斷如果某項沒有設定,則建立預設的選項
File cacheDir = StorageUtils.getCacheDirectory(context);
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(context)
.memoryCacheExtraOptions(480, 800) //解析圖檔時使用的最大尺寸,預設為裝置螢幕寬高
.discCacheExtraOptions(480, 800, CompressFormat.JPEG, 75, null) //從網絡下載下傳圖檔後儲存到磁盤時使用的圖檔尺寸及壓縮方法,如果不設定則儲存原始圖檔
.taskExecutor(...) //加載顯示圖檔任務,預設建立一個以FIFO(先進先出)方式排程線程池的任務
.taskExecutorForCachedImages(...) //顯示圖檔任務(隻針對已緩存到磁盤的圖檔),預設建立一個以FIFO(先進先出)方式排程線程池的任務
.threadPoolSize(3) // 線程池大小,這裡是預設值(注意不要設太大,否則會有OOM問題)
.threadPriority(Thread.NORM_PRIORITY - 1) // 線程優先級,這裡是預設值
.tasksProcessingOrder(QueueProcessingType.FIFO) // 線程排程方式,這裡是預設值
.denyCacheImageMultipleSizesInMemory() //關閉記憶體緩存同一張圖檔不同情況尺寸,對于同一張圖檔,當有不同尺寸緩存時後面尺寸會覆寫前面的尺寸
.memoryCache(new LruMemoryCache(2 * 1024 * 1024)) //設定記憶體緩存,預設會建立緩存大小為應用可用記憶體總大小1/8的LruMemoryCache
.memoryCacheSize(2 * 1024 * 1024) //設定記憶體緩存大小
.memoryCacheSizePercentage(13) // 設定記憶體緩存大小在應用可用總記憶體大小的比例
.discCache(new UnlimitedDiscCache(cacheDir)) // 磁盤緩存,這裡是預設方式
.discCacheSize(50 * 1024 * 1024) //磁盤緩存大小
.discCacheFileCount(100) //磁盤緩存檔案數量(如果使用FileCountLimitedDiscCache)
.discCacheFileNameGenerator(new HashCodeFileNameGenerator()) // 磁盤緩存時檔案命名方法,這裡是預設值。另外該庫還提供了MD5方式命名。
.imageDownloader(new BaseImageDownloader(context)) // 圖檔下載下傳器,這裡是預設方式
.imageDecoder(new BaseImageDecoder()) // 圖檔解碼器,這裡為預設方式
.defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // 圖檔顯示器,這裡是預設方式
.writeDebugLogs() //是否列印調試log
.build();
DisplayImageOptions 示例:
//這裡隻是列出各種情況的例子,在設定自己的選項時不要完全拷貝這裡的代碼
DisplayImageOptions options = new DisplayImageOptions.Builder()
.showImageOnLoading(R.drawable.ic_stub) // 加載圖檔時顯示的圖示
.showImageForEmptyUri(R.drawable.ic_empty) // uri為空時顯示的圖示
.showImageOnFail(R.drawable.ic_error) // 下載下傳圖檔失敗時顯示的圖示
.resetViewBeforeLoading(false) // 加載圖檔前是否清空ImageView,預設為false
.delayBeforeLoading(1000) //加載圖檔前延遲時間,預設為0
.cacheInMemory(false) // 是否啟用記憶體緩存,預設為false
.cacheOnDisc(false) // 是否啟用磁盤緩存,預設為false
.preProcessor(...) //圖檔處理函數,在圖檔緩存到記憶體之前運作
.postProcessor(...) //圖檔處理函數,在圖檔緩存到記憶體之後但顯示圖檔之前運作
.extraForDownloader(...) //下載下傳圖檔時額外資訊
.considerExifParams(false) // 解析圖檔時是否考慮JPEG圖檔的Exif資訊,預設為false
.imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // 解析圖檔時縮放因子,這裡是預設值
.bitmapConfig(Bitmap.Config.ARGB_8888) // 解析圖檔時采用的色彩格式,這裡是預設值
.decodingOptions(...) //解析圖檔時的option參數,該參數會覆寫上面的bitmapConfig中的參數,但其中的inSampleSize會被忽略,其采用imageScaleType設定的值
.displayer(new SimpleBitmapDisplayer()) // 圖檔顯示器,這裡是預設方式
.handler(new Handler()) // UI線程handler,用于綁定ImageView和Bitmap
.build();
目錄結構
ImageLoaderEngine: 控制并發任務
LoadAndDisplayImageTask:加載和顯示圖檔任務
xxxImageDownloader: 負責從網絡,檔案等擷取圖檔資料流
xxxImageDecoder:解析圖檔
xxxBitmapDisplayer: 負責顯示圖檔效果
SimpleBitmapDisplayer:直接顯示圖檔
FadeInBitmapDisplayer:淡入淡出效果
RoundedBitmapDisplayer:顯示圓角
RoundedVignetteBitmapDisplayer:圓角帶光暈效果
Cache: 緩存
MemoryCache: 記憶體緩存,通常使用元素為<String,Bitmap> 的Map來維護緩存内容
WeakMemoryCache:弱引用緩存,簡單地對每個bitmap做弱引用
FuzzyKeyMemoryCache:可以傳入比較規則,将不同的key視作相同,這樣Map中新加元素時會将原來對應key的value覆寫
LruMemoryCache:當緩存容量超過最大值時,删除最近最少使用的對象
LRULimitedMemoryCache:當緩存容量超過最大值時,删除最近最少使用的對象
FIFOLimitedMemoryCache:當緩存容量超過最大值時,删除最先放入隊列的對象
LargestLimitedMemoryCache:當緩存容量超過最大值時,删除目前最大的對象
LimitedAgeMemoryCache:當緩存容量超過最大值時,删除存活時間最長的對象
UsingFreqLimitedMemoryCache:當緩存容量超過最大值時,删除最不常使用的對象
DiscCache: 磁盤緩存,使用元素為<String,Key>的Map來維護緩存内容
UnlimitedDiscCache:無限制
LimitedAgeDiscCache:當緩存對象緩存時間超過設定值時,将會被删除
TotalSizeLimitedDiscCache:當緩存容量超過最大值時,删除最不活躍的對象(上一次使用時間最早的對象)
FileCountLimitedDiscCache:當緩存檔案數量超過最大值時,删除最不活躍的對象(上一次使用時間最早的對象)
緩存類圖:
ImageLoaderEngine 中包含三個Executor成員對象,每個都帶有線程池,其中一個Executor作為排程者用于管理另外兩個Executor
另兩個Executor一個用于下載下傳網絡圖檔,另一個專門處理已經在磁盤緩存過的圖檔。
整體流程圖:
以上是關于UIL異步緩存架構的大體介紹,源碼這裡就不貼出來了,感興趣的讀者可以自己研究一下。
網上有很多詳細介紹的,這裡推薦幾個:
Android 開源架構Universal-Image-Loader完全解析:
http://blog.csdn.net/xiaanming/article/details/26810303
Android 架構練成 教你打造高效的圖檔加載架構 :
http://blog.csdn.net/lmj623565791/article/details/41874561