Android 開源專案原始碼解析 -->Android Universal Image Loader 原始碼分析(十四)
專案:Android-Universal-Image-Loader
1. 功能介紹
1.1 Android Universal Image Loader
Android Universal Image Loader 是一個強大的、可高度定製的圖片快取,本文簡稱為UIL
。
簡單的說 UIL 就做了一件事——獲取圖片並顯示在相應的控制元件上。
1.2 基本使用
1.2.1 初始化
新增完依賴後在Application
或Activity
中初始化ImageLoader
,如下:
public class YourApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
ImageLoaderConfiguration configuration = new ImageLoaderConfiguration.Builder(this)
// 新增你的配置需求
.build();
ImageLoader.getInstance().init(configuration);
}
}
其中 configuration 表示ImageLoader
的配置資訊,可包括圖片最大尺寸、執行緒池、快取、下載器、解碼器等等。
1.2.2 Manifest 配置
<manifest>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application
android:name=".YourApplication"
…… >
……
</application>
</manifest>
新增網路許可權。如果允許磁碟快取,需要新增寫外設的許可權。
1.2.3 下載顯示圖片
下載圖片,解析為 Bitmap 並在 ImageView 中顯示。
imageLoader.displayImage(imageUri, imageView);
下載圖片,解析為 Bitmap 傳遞給回撥介面。
imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() {
@Override
public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
// 圖片處理
}
});
以上是簡單使用,更復雜 API 見本文詳細設計
。
1.3 特點
- 可配置度高。支援任務執行緒池、下載器、解碼器、記憶體及磁碟快取、顯示選項等等的配置。
- 包含記憶體快取和磁碟快取兩級快取。
- 支援多執行緒,支援非同步和同步載入。
- 支援多種快取演算法、下載進度監聽、ListView 圖片錯亂解決等。
2. 總體設計
2.1. 總體設計圖
上面是 UIL 的總體設計圖。整個庫分為ImageLoaderEngine
,Cache
及ImageDownloader
,ImageDecoder
,BitmapDisplayer
,BitmapProcessor
五大模組,其中Cache
分為MemoryCache
和DiskCache
兩部分。
簡單的講就是ImageLoader
收到載入及顯示圖片的任務,並將它交給ImageLoaderEngine
,ImageLoaderEngine
分發任務到具體執行緒池去執行,任務通過Cache
及ImageDownloader
獲取圖片,中間可能經過BitmapProcessor
和ImageDecoder
處理,最終轉換為Bitmap
交給BitmapDisplayer
在ImageAware
中顯示。
2.2. UIL 中的概念
簡單介紹一些概念,在4. 詳細設計
中會仔細介紹。
ImageLoaderEngine:任務分發器,負責分發LoadAndDisplayImageTask
和ProcessAndDisplayImageTask
給具體的執行緒池去執行,本文中也稱其為engine
,具體參考4.2.6
ImageLoaderEngine.java
。
ImageAware:顯示圖片的物件,可以是ImageView
等,具體參考4.2.9
ImageAware.java
。
ImageDownloader:圖片下載器,負責從圖片的各個來源獲取輸入流, 具體參考4.2.22 ImageDownloader.java
。
Cache:圖片快取,分為MemoryCache
和DiskCache
兩部分。
MemoryCache:記憶體圖片快取,可向記憶體快取快取圖片或從記憶體快取讀取圖片,具體參考4.2.24 MemoryCache.java
。
DiskCache:本地圖片快取,可向本地磁碟快取儲存圖片或從本地磁碟讀取圖片,具體參考4.2.38 DiskCache.java
。
ImageDecoder:圖片解碼器,負責將圖片輸入流InputStream
轉換為Bitmap
物件,
具體參考4.2.53 ImageDecoder.java
。
BitmapProcessor:圖片處理器,負責從快取讀取或寫入前對圖片進行處理。具體參考4.2.61 BitmapProcessor.java
。
BitmapDisplayer:將Bitmap
物件顯示在相應的控制元件ImageAware
上,
具體參考4.2.56 BitmapDisplayer.java
。
LoadAndDisplayImageTask:用於載入並顯示圖片的任務, 具體參考4.2.20 LoadAndDisplayImageTask.java
。
ProcessAndDisplayImageTask:用於處理並顯示圖片的任務, 具體參考4.2.19 ProcessAndDisplayImageTask.java
。
DisplayBitmapTask:用於顯示圖片的任務, 具體參考4.2.18 DisplayBitmapTask.java
。
3. 流程圖
上圖為圖片載入及顯示流程圖,在 uil 庫中給出,這裡用中文重新畫出。
4. 詳細設計
4.1 類關係圖
4.2 核心類功能介紹
4.2.1 ImageLoader.java
圖片載入器,對外的主要 API,採取了單例模式,用於圖片的載入和顯示。
主要函式:
(1). getInstance()
得到ImageLoader
的單例。通過雙層是否為 null 判斷提高效能。
(2). init(ImageLoaderConfiguration configuration)
初始化配置引數,引數configuration
為ImageLoader
的配置資訊,包括圖片最大尺寸、任務執行緒池、磁碟快取、下載器、解碼器等等。
實現中會初始化ImageLoaderEngine engine
屬性,該屬性為任務分發器。
(3). displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, ImageLoadingListener listener, ImageLoadingProgressListener progressListener)
載入並顯示圖片或載入並執行回撥介面。ImageLoader
載入圖片主要分為三類介面:
displayImage(…)
表示非同步載入並顯示圖片到對應的ImageAware
上。loadImage(…)
表示非同步載入圖片並執行回撥介面。loadImageSync(…)
表示同步載入圖片。
以上三類介面最終都會呼叫到這個函式進行圖片載入。函式引數解釋如下:
uri: 圖片的 uri。uri 支援多種來源的圖片,包括 http、https、file、content、assets、drawable 及自定義,具體介紹可見ImageDownloader
。
imageAware: 一個介面,表示需要載入圖片的物件,可包裝 View。
options: 圖片顯示的配置項。比如載入前、載入中、載入失敗應該顯示的佔點陣圖片,圖片是否需要在磁碟快取,是否需要在記憶體快取等。
listener: 圖片載入各種時刻的回撥介面,包括開始載入、載入失敗、載入成功、取消載入四個時刻的回撥函式。
progressListener: 圖片載入進度的回撥介面。
函式流程圖如下:
4.2.2 ImageLoaderConfiguration.java
ImageLoader
的配置資訊,包括圖片最大尺寸、執行緒池、快取、下載器、解碼器等等。
主要屬性:
(1). Resources resources
程式本地資源訪問器,用於載入DisplayImageOptions
中設定的一些 App 中圖片資源。
(2). int maxImageWidthForMemoryCache
記憶體快取的圖片最大寬度。
(3). int maxImageHeightForMemoryCache
記憶體快取的圖片最大高度。
(4). int maxImageWidthForDiskCache
磁碟快取的圖片最大寬度。
(5). int maxImageHeightForDiskCache
磁碟快取的圖片最大高度。
(6). BitmapProcessor processorForDiskCache
圖片處理器,用於處理從磁碟快取中讀取到的圖片。
(7). Executor taskExecutor
ImageLoaderEngine
中用於執行從源獲取圖片任務的 Executor。
(18). Executor taskExecutorForCachedImages
ImageLoaderEngine
中用於執行從快取獲取圖片任務的 Executor。
(19). boolean customExecutor
使用者是否自定義了上面的 taskExecutor。
(20). boolean customExecutorForCachedImages
使用者是否自定義了上面的 taskExecutorForCachedImages。
(21). int threadPoolSize
上面兩個預設執行緒池的核心池大小,即最大併發數。
(22). int threadPriority
上面兩個預設執行緒池的執行緒優先順序。
(23). QueueProcessingType tasksProcessingType
上面兩個預設執行緒池的執行緒佇列型別。目前只有 FIFO, LIFO 兩種可供選擇。
(24). MemoryCache memoryCache
圖片記憶體快取。
(25). DiskCache diskCache
圖片磁碟快取,一般放在 SD 卡。
(26). ImageDownloader downloader
圖片下載器。
(27). ImageDecoder decoder
圖片解碼器,內部可使用我們常用的BitmapFactory.decode(…)
將圖片資源解碼成Bitmap
物件。
(28). DisplayImageOptions defaultDisplayImageOptions
圖片顯示的配置項。比如載入前、載入中、載入失敗應該顯示的佔點陣圖片,圖片是否需要在磁碟快取,是否需要在記憶體快取等。
(29). ImageDownloader networkDeniedDownloader
不允許訪問網路的圖片下載器。
(30). ImageDownloader slowNetworkDownloader
慢網路情況下的圖片下載器。
4.2.3 ImageLoaderConfiguration.Builder.java 靜態內部類
Builder 模式,用於構造引數繁多的ImageLoaderConfiguration
。
其屬性與ImageLoaderConfiguration
類似,函式多是屬性設定函式。
主要函式及含義:
(1). build()
按照配置,生成 ImageLoaderConfiguration。程式碼如下:
public ImageLoaderConfiguration build() {
initEmptyFieldsWithDefaultValues();
return new ImageLoaderConfiguration(this);
}
(2). initEmptyFieldsWithDefaultValues()
初始化值為null
的屬性。若使用者沒有配置相關項,UIL 會通過呼叫DefaultConfigurationFactory
中的函式返回一個預設值當配置。
taskExecutorForCachedImages
、taskExecutor
及ImageLoaderEngine
的taskDistributor
的預設值如下:
parameters | taskDistributor | taskExecutorForCachedImages/taskExecutor |
---|---|---|
corePoolSize | 0 | 3 |
maximumPoolSize | Integer.MAX_VALUE | 3 |
keepAliveTime | 60 | 0 |
unit | SECONDS | MILLISECONDS |
workQueue | SynchronousQueue | LIFOLinkedBlockingDeque / LinkedBlockingQueue |
priority | 5 | 3 |
diskCacheFileNameGenerator
預設值為HashCodeFileNameGenerator
。
memoryCache
預設值為LruMemoryCache
。如果記憶體快取不允許快取一張圖片的多個尺寸,則用FuzzyKeyMemoryCache
做封裝,同一個圖片新的尺寸會覆蓋快取中該圖片老的尺寸。
diskCache
預設值與diskCacheSize
和diskCacheFileCount
值有關,如果他們有一個大於
0,則預設為LruDiskCache
,否則使用無大小限制的UnlimitedDiskCache
。
downloader
預設值為BaseImageDownloader
。
decoder
預設值為BaseImageDecoder
。
詳細及其他屬性預設值請到DefaultConfigurationFactory
中檢視。
(3). denyCacheImageMultipleSizesInMemory()
設定記憶體快取不允許快取一張圖片的多個尺寸,預設允許。
後面會講到 View 的 getWidth()
在初始化前後的不同值與這個設定的關係。
(4). diskCacheSize(int maxCacheSize)
設定磁碟快取的最大位元組數,如果大於 0 或者下面的maxFileCount
大於 0,預設的DiskCache
會用LruDiskCache
,否則使用無大小限制的UnlimitedDiskCache
。
(5). diskCacheFileCount(int maxFileCount)
設定磁碟快取資料夾下最大檔案數,如果大於 0 或者上面的maxCacheSize
大於 0,預設的DiskCache
會用LruDiskCache
,否則使用無大小限制的UnlimitedDiskCache
。
4.2.4 ImageLoaderConfiguration.NetworkDeniedImageDownloader.java 靜態內部類
不允許訪問網路的圖片下載器,實現了ImageDownloader
介面。
實現也比較簡單,包裝一個ImageDownloader
物件,通過在 getStream(…) 函式中禁止 Http 和 Https Scheme 禁止網路訪問,如下:
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
throw new IllegalStateException();
default:
return wrappedDownloader.getStream(imageUri, extra);
}
}
4.2.5 ImageLoaderConfiguration.SlowNetworkImageDownloader.java 靜態內部類
慢網路情況下的圖片下載器,實現了ImageDownloader
介面。
通過包裝一個ImageDownloader
物件實現,在 getStream(…) 函式中當 Scheme 為 Http 和 Https 時,用FlushedInputStream
代替InputStream
處理慢網路情況,具體見後面FlushedInputStream
的介紹。
4.2.6 ImageLoaderEngine.java
LoadAndDisplayImageTask
和ProcessAndDisplayImageTask
任務分發器,負責分發任務給具體的執行緒池。
主要屬性:
(1). ImageLoaderConfiguration configuration
ImageLoader
的配置資訊,可包括圖片最大尺寸、執行緒池、快取、下載器、解碼器等等。
(2). Executor taskExecutor
用於執行從源獲取圖片任務的 Executor,為configuration
中的 taskExecutor,如果為null
,則會呼叫DefaultConfigurationFactory.createExecutor(…)
根據配置返回一個預設的執行緒池。
(3). Executor taskExecutorForCachedImages
用於執行從快取獲取圖片任務的 Executor,為configuration
中的 taskExecutorForCachedImages,如果為null
,則會呼叫DefaultConfigurationFactory.createExecutor(…)
根據配置返回一個預設的執行緒池。
(4). Executor taskDistributor
任務分發執行緒池,任務指LoadAndDisplayImageTask
和ProcessAndDisplayImageTask
,因為只需要分發給上面的兩個
Executor 去執行任務,不存在較耗時或阻塞操作,所以用無併發數(Int 最大值)限制的執行緒池即可。
(5). Map cacheKeysForImageAwares
ImageAware
與記憶體快取 key 對應的 map,key 為ImageAware
的
id,value 為記憶體快取的 key。
(6). Map uriLocks
圖片正在載入的重入鎖 map,key 為圖片的 uri,value 為標識其正在載入的重入鎖。
(7). AtomicBoolean paused
是否被暫停。如果為true
,則所有新的載入或顯示任務都會等待直到取消暫停(為false
)。
(8). AtomicBoolean networkDenied
是否不允許訪問網路,如果為true
,通過ImageLoadingListener.onLoadingFailed(…)
獲取圖片,則所有不在快取中需要網路訪問的請求都會失敗,返回失敗原因為網路訪問被禁止
。
(9). AtomicBoolean slowNetwork
是否是慢網路情況,如果為true
,則自動呼叫SlowNetworkImageDownloader
下載圖片。
(10). Object pauseLock
暫停的等待鎖,可在engine
被暫停後呼叫這個鎖等待。
主要函式:
(1). void submit(final LoadAndDisplayImageTask task)
新增一個LoadAndDisplayImageTask
。直接用taskDistributor
執行一個
Runnable,在 Runnable 內部根據圖片是否被磁碟快取過確定使用taskExecutorForCachedImages
還是taskExecutor
執行該
task。
(2). void submit(ProcessAndDisplayImageTask task)
新增一個ProcessAndDisplayImageTask
。直接用taskExecutorForCachedImages
執行該
task。
(3). void pause()
暫停圖片載入任務。所有新的載入或顯示任務都會等待直到取消暫停(為false
)。
(4). void resume()
繼續圖片載入任務。
(5). stop()
暫停所有載入和顯示圖片任務並清除這裡的內部屬性值。
(6). fireCallback(Runnable r)
taskDistributor
立即執行某個任務。
(7). getLockForUri(String uri)
得到某個 uri 的重入鎖,如果不存在則新建。
(8). createTaskExecutor()
呼叫DefaultConfigurationFactory.createExecutor(…)
建立一個執行緒池。
(9). getLoadingUriForView(ImageAware imageAware)
得到某個imageAware
正在載入的圖片 uri。
(10). prepareDisplayTaskFor(ImageAware imageAware, String memoryCacheKey)
準備開始一個Task
。向cacheKeysForImageAwares
中插入ImageAware
的
id 和圖片在記憶體快取中的 key。
(11). void cancelDisplayTaskFor(ImageAware imageAware)
取消一個顯示任務。從cacheKeysForImageAwares
中刪除ImageAware
對應元素。
(12). denyNetworkDownloads(boolean denyNetworkDownloads)
設定是否不允許網路訪問。
(13). handleSlowNetwork(boolean handleSlowNetwork)
設定是否慢網路情況。
4.2.7 DefaultConfigurationFactory.java
為ImageLoaderConfiguration
及ImageLoaderEngine
提供一些預設配置。
主要函式:
(1). createExecutor(int threadPoolSize, int threadPriority, QueueProcessingType tasksProcessingType)
建立執行緒池。
threadPoolSize
表示核心池大小(最大併發數)。
threadPriority
表示執行緒優先順序。
tasksProcessingType
表示執行緒佇列型別,目前只有 FIFO, LIFO 兩種可供選擇。
內部實現會呼叫createThreadFactory(…)
返回一個支援執行緒優先順序設定,並且以固定規則命名新建的執行緒的執行緒工廠類DefaultConfigurationFactory.DefaultThreadFactory
。
(2). createTaskDistributor()
為ImageLoaderEngine
中的任務分發器taskDistributor
提供執行緒池,該執行緒池為
normal 優先順序的無併發大小限制的執行緒池。
(3). createFileNameGenerator()
返回一個HashCodeFileNameGenerator
物件,即以 uri HashCode 為檔名的檔名生成器。
(4). createDiskCache(Context context, FileNameGenerator diskCacheFileNameGenerator, long diskCacheSize, int diskCacheFileCount)
建立一個 Disk Cache。如果 diskCacheSize 或者 diskCacheFileCount 大於 0,返回一個LruDiskCache
,否則返回無大小限制的UnlimitedDiskCache
。
(5). createMemoryCache(Context context, int memoryCacheSize)
建立一個 Memory Cache。返回一個LruMemoryCache
,若 memoryCacheSize 為 0,則設定該記憶體快取的最大位元組數為 App 最大可用記憶體的 1/8。
這裡 App 的最大可用記憶體也支援系統在 Honeycomb 之後(ApiLevel >= 11) application 中android:largeHeap="true"
的設定。
(6). createImageDownloader(Context context)
建立圖片下載器,返回一個BaseImageDownloader
。
(7). createImageDecoder(boolean loggingEnabled)
建立圖片解碼器,返回一個BaseImageDecoder
。
(8). createBitmapDisplayer()
建立圖片顯示器,返回一個SimpleBitmapDisplayer
。
4.2.8 DefaultConfigurationFactory.DefaultThreadFactory
預設的執行緒工廠類,為
DefaultConfigurationFactory.createExecutor(…)
和
DefaultConfigurationFactory.createTaskDistributor(…)
提供執行緒工廠。支援執行緒優先順序設定,並且以固定規則命名新建的執行緒。
PS:重新命名執行緒是個很好的習慣,它的一大作用就是方便問題排查,比如效能優化,用 TraceView 檢視執行緒,根據名字很容易分辨各個執行緒。
4.2.9 ImageAware.java
需要顯示圖片的物件的介面,可包裝 View 表示某個需要顯示圖片的 View。
主要函式:
(1). View getWrappedView()
得到被包裝的 View,圖片在該 View 上顯示。
(2). getWidth() 與 getHeight()
得到寬度高度,在計算圖片縮放比例時會用到。
(3). getId()
得到唯一標識 id。ImageLoaderEngine
中用這個 id 標識正在載入圖片的ImageAware
和圖片記憶體快取
key 的對應關係,圖片請求前會將記憶體快取 key 與新的記憶體快取 key 進行比較,如果不相等,則之前的圖片請求會被取消。這樣當ImageAware
被複用時就不會因非同步載入(前面任務未取消)而造成錯亂了。
4.2.10 ViewAware.java
封裝 Android View 來顯示圖片的抽象類,實現了ImageAware
介面,利用Reference
來
Warp View 防止記憶體洩露。
主要函式:
(1). ViewAware(View view, boolean checkActualViewSize)
建構函式。
view
表示需要顯示圖片的物件。
checkActualViewSize
表示通過getWidth()
和getHeight()
獲取圖片寬高時返回真實的寬和高,還是LayoutParams
的寬高,true
表示返回真實寬和高。
如果為true
會導致一個問題,View
在還沒有初始化完成時載入圖片,這時它的真實寬高為
0,會取它LayoutParams
的寬高,而圖片快取的 key 與這個寬高有關,所以當View
初始化完成再次需要載入該圖片時,getWidth()
和getHeight()
返回的寬高都已經變化,快取
key 不一樣,從而導致快取命中失敗會再次從網路下載一次圖片。可通過ImageLoaderConfiguration.Builder.denyCacheImageMultipleSizesInMemory()
設定不允許記憶體快取快取一張圖片的多個尺寸。
(2). setImageDrawable(Drawable drawable)
如果當前操作在主執行緒並且 View 沒有被回收,則呼叫抽象函式setImageDrawableInto(Drawable drawable, View view)
去向View
設定圖片。
(3). setImageBitmap(Bitmap bitmap)
如果當前操作在主執行緒並且 View 沒有被回收,則呼叫抽象函式setImageBitmapInto(Bitmap bitmap, View view)
去向View
設定圖片。
4.2.11 ImageViewAware.java
封裝 Android ImageView 來顯示圖片的ImageAware
,繼承了ViewAware
,利用Reference
來
Warp View 防止記憶體洩露。
如果getWidth()
函式小於等於 0,會利用反射獲取mMaxWidth
的值作為寬。
如果getHeight()
函式小於等於 0,會利用反射獲取mMaxHeight
的值作為高。
4.2.12 NonViewAware.java
僅包含處理圖片相關資訊卻沒有需要顯示圖片的 View 的ImageAware
,實現了ImageAware
介面。常用於載入圖片後呼叫回撥介面而不是顯示的情況。
4.2.13 DisplayImageOptions.java
圖片顯示的配置項。比如載入前、載入中、載入失敗應該顯示的佔點陣圖片,圖片是否需要在磁碟快取,是否需要在 memory 快取等。
主要屬性及含義:
(1). int imageResOnLoading
圖片正在載入中的佔點陣圖片的 resource id,優先順序比下面的imageOnLoading
高,當存在時,imageOnLoading
不起作用。
(2). int imageResForEmptyUri
空 uri 時的佔點陣圖片的 resource id,優先順序比下面的imageForEmptyUri
高,當存在時,imageForEmptyUri
不起作用。
(3). int imageResOnFail
載入失敗時的佔點陣圖片的 resource id,優先順序比下面的imageOnFail
高,當存在時,imageOnFail
不起作用。
(4). Drawable imageOnLoading
載入中的佔點陣圖片的 drawabled 物件,預設為 null。
(5). Drawable imageForEmptyUri
空 uri 時的佔點陣圖片的 drawabled 物件,預設為 null。
(6). Drawable imageOnFail
載入失敗時的佔點陣圖片的 drawabled 物件,預設為 null。
(7). boolean resetViewBeforeLoading
在載入前是否重置 view,通過 Builder 構建的物件預設為 false。
(8). boolean cacheInMemory
是否快取在記憶體中,通過 Builder 構建的物件預設為 false。
(9). boolean cacheOnDisk
是否快取在磁碟中,通過 Builder 構建的物件預設為 false。
(10). ImageScaleType imageScaleType
圖片的縮放型別,通過 Builder 構建的物件預設為IN_SAMPLE_POWER_OF_2
。
(11). Options decodingOptions;
為 BitmapFactory.Options,用於BitmapFactory.decodeStream(imageStream, null, decodingOptions)
得到圖片尺寸等資訊。
(12). int delayBeforeLoading
設定在開始載入前的延遲時間,單位為毫秒,通過 Builder 構建的物件預設為 0。
(13). boolean considerExifParams
是否考慮圖片的 EXIF 資訊,通過 Builder 構建的物件預設為 false。
(14). Object extraForDownloader
下載器需要的輔助資訊。下載時傳入ImageDownloader.getStream(String, Object)
的物件,方便使用者自己擴充套件,預設為 null。
(15). BitmapProcessor preProcessor
快取在記憶體之前的處理程式,預設為 null。
(16). BitmapProcessor postProcessor
快取在記憶體之後的處理程式,預設為 null。
(17). BitmapDisplayer displayer
圖片的顯示方式,通過 Builder 構建的物件預設為SimpleBitmapDisplayer
。
(18). Handler handler
handler 物件,預設為 null。
(19). boolean isSyncLoading
是否同步載入,通過 Builder 構建的物件預設為 false。
4.2.14 DisplayImageOptions.Builder.java 靜態內部類
Builder 模式,用於構造引數繁多的DisplayImageOptions
。
其屬性與DisplayImageOptions
類似,函式多是屬性設定函式。
4.2.15 ImageLoadingListener.java
圖片載入各種時刻的回撥介面,可在圖片載入的某些點做監聽。
包括開始載入(onLoadingStarted)、載入失敗(onLoadingFailed)、載入成功(onLoadingComplete)、取消載入(onLoadingCancelled)四個回撥函式。
4.2.16 SimpleImageLoadingListener.java
實現ImageLoadingListener
介面,不過各個函式都是空實現,表示不在 Image 載入過程中做任何回撥監聽。
ImageLoader.displayImage(…)
函式中當入參listener
為空時的預設值。
4.2.17 ImageLoadingProgressListener.java
Image 載入進度的回撥介面。其中抽象函式
void onProgressUpdate(String imageUri, View view, int current, int total)
會在獲取圖片儲存到檔案系統時被回撥。其中total
表示圖片總大小,為網路請求結果Response
Header
中content-length
欄位,如果不存在則為 -1。
4.2.18 DisplayBitmapTask.java
顯示圖片的Task
,實現了Runnable
介面,必須在主執行緒呼叫。
主要函式:
(1) run()
首先判斷imageAware
是否被 GC 回收,如果是直接呼叫取消載入回撥介面ImageLoadingListener.onLoadingCancelled(…)
;
否則判斷imageAware
是否被複用,如果是直接呼叫取消載入回撥介面ImageLoadingListener.onLoadingCancelled(…)
;
否則呼叫displayer
顯示圖片,並將imageAware
從正在載入的
map 中移除。呼叫載入成功回撥介面ImageLoadingListener.onLoadingComplete(…)
。
對於 ListView 或是 GridView 這類會快取 Item 的 View 來說,單個 Item 中如果含有 ImageView,在滑動過程中可能因為非同步載入及 View 複用導致圖片錯亂,這裡對imageAware
是否被複用的判斷就能很好的解決這個問題。原因類似:Android
ListView 滑動過程中圖片顯示重複錯位閃爍問題原因及解決方案。
4.2.19 ProcessAndDisplayImageTask.java
處理並顯示圖片的Task
,實現了Runnable
介面。
主要函式:
(1) run()
主要通過 imageLoadingInfo 得到BitmapProcessor
處理圖片,並用處理後的圖片和配置新建一個DisplayBitmapTask
在ImageAware
中顯示圖片。
4.2.20 LoadAndDisplayImageTask.java
載入並顯示圖片的Task
,實現了Runnable
介面,用於從網路、檔案系統或記憶體獲取圖片並解析,然後呼叫DisplayBitmapTask
在ImageAware
中顯示圖片。
主要函式:
(1) run()
獲取圖片並顯示,核心程式碼如下:
bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp == null || bmp.isRecycled()) {
bmp = tryLoadBitmap();
...
...
...
if (bmp != null && options.isCacheInMemory()) {
L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
configuration.memoryCache.put(memoryCacheKey, bmp);
}
}
……
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
runTask(displayBitmapTask, syncLoading, handler, engine);
從上面程式碼段中可以看到先是從記憶體快取中去讀取 bitmap 物件,若 bitmap 物件不存在,則呼叫 tryLoadBitmap() 函式獲取 bitmap 物件,獲取成功後若在 DisplayImageOptions.Builder 中設定了 cacheInMemory(true), 同時將 bitmap 物件快取到記憶體中。
最後新建DisplayBitmapTask
顯示圖片。
函式流程圖如下:
- 判斷圖片的記憶體快取是否存在,若存在直接執行步驟 8;
- 判斷圖片的磁碟快取是否存在,若存在直接執行步驟 5;
- 從網路上下載圖片;
- 將圖片快取在磁碟上;
- 將圖片 decode 成 bitmap 物件;
- 根據
DisplayImageOptions
配置對圖片進行預處理(Pre-process Bitmap); - 將 bitmap 物件快取到記憶體中;
- 根據
DisplayImageOptions
配置對圖片進行後處理(Post-process Bitmap); - 執行
DisplayBitmapTask
將圖片顯示在相應的控制元件上。
流程圖可以參見3. 流程圖
。
(2) tryLoadBitmap()
從磁碟快取或網路獲取圖片,核心程式碼如下:
File imageFile = configuration.diskCache.get(uri);
if (imageFile != null && imageFile.exists()) {
...
bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
}
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
...
String imageUriForDecoding = uri;
if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {
imageFile = configuration.diskCache.get(uri);
if (imageFile != null) {
imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());
}
}
checkTaskNotActual();
bitmap = decodeImage(imageUriForDecoding);
...
}
首先根據 uri 看看磁碟中是不是已經快取了這個檔案,如果已經快取,呼叫 decodeImage 函式,將圖片檔案 decode 成 bitmap 物件; 如果 bitmap 不合法或快取檔案不存在,判斷是否需要快取在磁碟,需要則呼叫tryCacheImageOnDisk()
函式去下載並快取圖片到本地磁碟,再通過decodeImage(imageUri)
函式將圖片檔案
decode 成 bitmap 物件,否則直接通過decodeImage(imageUriForDecoding)
下載圖片並解析。
(3) tryCacheImageOnDisk()
下載圖片並儲存在磁碟內,根據磁碟快取圖片最長寬高的配置處理圖片。
loaded = downloadImage();
主要就是這一句話,呼叫下載器下載並儲存圖片。
如果你在ImageLoaderConfiguration
中還配置了maxImageWidthForDiskCache
或者maxImageHeightForDiskCache
,還會呼叫resizeAndSaveImage()
函式,調整圖片尺寸,並儲存新的圖片檔案。
(4) downloadImage()
下載圖片並儲存在磁碟內。呼叫getDownloader()
得到ImageDownloader
去下載圖片。
(4) resizeAndSaveImage(int maxWidth, int maxHeight)
從磁碟快取中得到圖片,重新設定大小及進行一些處理後儲存。
(5) getDownloader()
根據ImageLoaderEngine
配置得到下載器。
如果不允許訪問網路,則使用不允許訪問網路的圖片下載器NetworkDeniedImageDownloader
;如果是慢網路情況,則使用慢網路情況下的圖片下載器SlowNetworkImageDownloader
;否則直接使用ImageLoaderConfiguration
中的downloader
。
4.2.21 ImageLoadingInfo.java
載入和顯示圖片任務需要的資訊。
String uri
圖片 url。
String memoryCacheKey
圖片快取 key。
ImageAware imageAware
需要載入圖片的物件。
ImageSize targetSize
圖片的顯示尺寸。
DisplayImageOptions options
圖片顯示的配置項。
ImageLoadingListener listener
圖片載入各種時刻的回撥介面。
ImageLoadingProgressListener progressListener
圖片載入進度的回撥介面。
ReentrantLock loadFromUriLock
圖片載入中的重入鎖。
4.2.22 ImageDownloader.java
圖片下載介面。待實現函式
getStream(String imageUri, Object extra)
表示通過 uri 得到 InputStream。
通過內部定義的列舉Scheme
, 可以看出 UIL 支援哪些圖片來源。
HTTP("http"), HTTPS("https"), FILE("file"), CONTENT("content"), ASSETS("assets"), DRAWABLE("drawable"), UNKNOWN("");
4.2.23 BaseImageDownloader.java
ImageDownloader
的具體實現類。得到上面各種Scheme
對應的圖片
InputStream。
主要函式
(1). getStream(String imageUri, Object extra)
在getStream(…)
函式內根據不同Scheme
型別獲取圖片輸入流。
@Override
public InputStream getStream(String imageUri, Object extra) throws IOException {
switch (Scheme.ofUri(imageUri)) {
case HTTP:
case HTTPS:
return getStreamFromNetwork(imageUri, extra);
case FILE:
return getStreamFromFile(imageUri, extra);
case CONTENT:
return getStreamFromContent(imageUri, extra);
case ASSETS:
return getStreamFromAssets(imageUri, extra);
case DRAWABLE:
return getStreamFromDrawable(imageUri, extra);
case UNKNOWN:
default:
return getStreamFromOtherSource(imageUri, extra);
}
}
具體見下面各函式介紹。
(2). getStreamFromNetwork(String imageUri, Object extra)
通過HttpURLConnection
從網路獲取圖片的InputStream
。支援
response code 為 3xx 的重定向。這裡有個小細節程式碼如下:
try {
imageStream = conn.getInputStream();
} catch (IOException e) {
// Read all data to allow reuse connection (http://bit.ly/1ad35PY)
IoUtils.readAndCloseStream(conn.getErrorStream());
throw e;
}
在發生異常時會呼叫conn.getErrorStream()
繼續讀取 Error Stream,這是為了利於網路連線回收及複用。但有意思的是在 Froyo(2.2) 之前,HttpURLConnection
有個重大 Bug,呼叫 close() 函式會影響連線池,導致連線複用失效,不少庫通過在 2.3 之前使用 AndroidHttpClient 解決這個問題。
(3). getStreamFromFile(String imageUri, Object extra)
從檔案系統獲取圖片的InputStream
。如果 uri 是 video 型別,則需要單獨得到 video 的縮圖返回,否則按照一般讀取檔案操作返回。
(4). getStreamFromContent(String imageUri, Object extra)
從 ContentProvider 獲取圖片的InputStream
。
如果是 video 型別,則先從MediaStore
得到 video 的縮圖返回;
如果是聯絡人型別,通過ContactsContract.Contacts.openContactPhotoInputStream(res, uri)
讀取內容返回。
否則通過 ContentResolver.openInputStream(…) 讀取內容返回。
(5). getStreamFromAssets(String imageUri, Object extra)
從 Assets 中獲取圖片的InputStream
。
(6). getStreamFromDrawable(String imageUri, Object extra)
從 Drawable 資源中獲取圖片的InputStream
。
(7). getStreamFromOtherSource(String imageUri, Object extra)
UNKNOWN(自定義)型別的處理,目前是直接丟擲不支援的異常。
4.2.24 MemoryCache.java
Bitmap 記憶體快取介面,需要實現的介面包括 get(…)、put(…)、remove(…)、clear()、keys()。
4.2.25 BaseMemoryCache.java
實現了MemoryCache
主要函式的抽象類,以 Map\<string, reference\> softMap 做為快取池,利於虛擬機器在記憶體不足時回收快取物件。提供抽象函式:
protected abstract Reference<Bitmap> createReference(Bitmap value)
表示根據 Bitmap 建立一個 Reference 做為快取物件。Reference 可以是 WeakReference、SoftReference 等。
4.2.26 WeakMemoryCache.java
以WeakReference<Bitmap>
做為快取 value 的記憶體快取,實現了BaseMemoryCache
。
實現了BaseMemoryCache
的createReference(Bitmap
value)
函式,直接返回一個new WeakReference<Bitmap>(value)
做為快取 value。
4.2.27 LimitedMemoryCache.java
限制總位元組大小的記憶體快取,繼承自BaseMemoryCache
的抽象類。
會在 put(…) 函式中判斷總體大小是否超出了上限,是則迴圈刪除快取物件直到小於上限。刪除順序由抽象函式
protected abstract Bitmap removeNext()
決定。抽象函式
protected abstract int getSize(Bitmap value)
表示每個元素大小。
4.2.28 LargestLimitedMemoryCache.java
限制總位元組大小的記憶體快取,會在快取滿時優先刪除 size 最大的元素,繼承自LimitedMemoryCache
。
實現了LimitedMemoryCache
快取removeNext()
函式,總是返回當前快取中
size 最大的元素。
4.2.29 UsingFreqLimitedMemoryCache.java
限制總位元組大小的記憶體快取,會在快取滿時優先刪除使用次數最少的元素,繼承自LimitedMemoryCache
。
實現了LimitedMemoryCache
快取removeNext()
函式,總是返回當前快取中使用次數最少的元素。
4.2.30 LRULimitedMemoryCache.java
限制總位元組大小的記憶體快取,會在快取滿時優先刪除最近最少使用的元素,繼承自LimitedMemoryCache
。
通過new LinkedHashMap<String, Bitmap>(10, 1.1f, true)
作為快取池。LinkedHashMap 第三個參數列示是否需要根據訪問順序(accessOrder)排序,true
表示根據accessOrder
排序,最近訪問的跟最新加入的一樣放到最後面,false 表示根據插入順序排序。這裡為 true 且快取滿時始終刪除第一個元素,即始終刪除最近最少訪問的元素。
實現了LimitedMemoryCache
快取removeNext()
函式,總是返回第一個元素,即最近最少使用的元素。
4.2.31 FIFOLimitedMemoryCache.java
限制總位元組大小的記憶體快取,會在快取滿時優先刪除先進入快取的元素,繼承自LimitedMemoryCache
。
實現了LimitedMemoryCache
快取removeNext()
函式,總是返回最先進入快取的元素。
以上所有LimitedMemoryCache
子類都有個問題,就是 Bitmap 雖然通過WeakReference<Bitmap>
包裝,但實際根本不會被虛擬機器回收,因為他們子類中同時都保留了
Bitmap 的強引用。大都是 UIL 早期實現的版本,不推薦使用。
4.2.32 LruMemoryCache.java
限制總位元組大小的記憶體快取,會在快取滿時優先刪除最近最少使用的元素,實現了MemoryCache
。LRU(Least Recently Used) 為最近最少使用演算法。
以new LinkedHashMap<String, Bitmap>(0, 0.75f, true)
作為快取池。LinkedHashMap 第三個參數列示是否需要根據訪問順序(accessOrder)排序,true
表示根據accessOrder
排序,最近訪問的跟最新加入的一樣放到最後面,false 表示根據插入順序排序。這裡為 true 且快取滿時始終刪除第一個元素,即始終刪除最近最少訪問的元素。
在put(…)
函式中通過trimToSize(int
maxSize)
函式判斷總體大小是否超出了上限,是則刪除第快取池中第一個元素,即最近最少使用的元素,直到總體大小小於上限。
LruMemoryCache
功能上與LRULimitedMemoryCache
類似,不過在實現上更加優雅。用簡單的實現介面方式,而不是不斷繼承的方式。
4.2.33 LimitedAgeMemoryCache.java
限制了物件最長存活週期的記憶體快取。
MemoryCache
的裝飾者,相當於為MemoryCache
新增了一個特性。以一個MemoryCache
記憶體快取和一個
maxAge 做為建構函式入參。在 get(…) 時判斷如果物件存活時間已經超過設定的最長時間,則刪除。
4.2.34 FuzzyKeyMemoryCache.java
可以將某些原本不同的 key 看做相等
,在 put 時刪除這些相等
的
key。
MemoryCache
的裝飾者,相當於為MemoryCache
新增了一個特性。以一個MemoryCache
記憶體快取和一個
keyComparator 做為建構函式入參。在 put(…) 時判斷如果 key 與快取中已有 key 經過Comparator
比較後相等,則刪除之前的元素。
4.2.35 FileNameGenerator.java
根據 uri 得到檔名的介面。
4.2.36 HashCodeFileNameGenerator.java
以 uri 的 hashCode 作為檔名。
4.2.37 Md5FileNameGenerator.java
以 uri 的 MD5 值作為檔名。
4.2.38 DiskCache.java
圖片的磁碟快取介面。
主要函式:
(1) File get(String imageUri)
根據原始圖片的 uri 去獲取快取圖片的檔案。
(2) boolean save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener)
儲存 imageStream 到磁碟中,listener 表示儲存進度且可在其中取消某些段的儲存。
(3) boolean save(String imageUri, Bitmap bitmap)
儲存圖片到磁碟。
(4) boolean remove(String imageUri)
根據圖片 uri 刪除快取圖片。
(5) void close()
關閉磁碟快取,並釋放資源。
(6) void clear()
清空磁碟快取。
(7) File getDirectory()
得到磁碟快取的根目錄。
4.2.39 BaseDiskCache.java
一個無大小限制的本地圖片快取,實現了DiskCache
主要函式的抽象類。
圖片快取在cacheDir
資料夾內,當cacheDir
不可用時,則使用備庫reserveCacheDir
。
主要函式:
(1). save(String imageUri, InputStream imageStream, IoUtils.CopyListener listener)
先根據imageUri
得到目標檔案,將imageStream
先寫入與目標檔案同一資料夾的
.tmp 結尾的臨時檔案內,若未被listener
取消且寫入成功則將臨時檔案重新命名為目標檔案並返回 true,否則刪除臨時檔案並返回 false。
(2). save(String imageUri, Bitmap bitmap)
先根據imageUri
得到目標檔案,通過Bitmap.compress(…)
函式將bitmap
先寫入與目標檔案同一資料夾的
.tmp 結尾的臨時檔案內,若寫入成功則將臨時檔案重新命名為目標檔案並返回 true,否則刪除臨時檔案並返回 false。
(3). File getFile(String imageUri)
根據 imageUri 和 fileNameGenerator
得到檔名,返回cacheDir
內該檔案,若cacheDir
不可用,則使用備庫reserveCacheDir
。
4.2.40 LimitedAgeDiskCache.java
限制了快取物件最長存活週期的磁碟快取,繼承自BaseDiskCache
。
在 get(…) 時判斷如果快取物件存活時間已經超過設定的最長時間,則刪除。在 save(…) 時儲存當存時間作為物件的建立時間。
4.2.41 UnlimitedDiskCache.java
一個無大小限制的本地圖片快取。與BaseDiskCache
無異,只是用了個意思明確的類名。
4.2.42 DiskLruCache.java
限制總位元組大小的記憶體快取,會在快取滿時優先刪除最近最少使用的元素。
通過快取目錄下名為journal
的檔案記錄快取的所有操作,並在快取open
時讀取journal
的檔案內容儲存到LinkedHashMap<String,
Entry> lruEntries
中,後面get(String key)
獲取快取內容時,會先從lruEntries
中得到圖片檔名返回檔案。
LRU 的實現跟上面記憶體快取類似,lruEntries
為new
LinkedHashMap<String, Entry>(0, 0.75f, true)
,LinkedHashMap 第三個參數列示是否需要根據訪問順序(accessOrder)排序,true 表示根據accessOrder
排序,最近訪問的跟最新加入的一樣放到最後面,false
表示根據插入順序排序。這裡為 true 且快取滿時trimToSize()
函式始終刪除第一個元素,即始終刪除最近最少訪問的檔案。
來源於 JakeWharton 的開源專案 DiskLruCache,具體分析請等待 DiskLruCache 原始碼解析 完成。
4.2.43 LruDiskCache.java
限制總位元組大小的記憶體快取,會在快取滿時優先刪除最近最少使用的元素,實現了DiskCache
。
內部有個DiskLruCache cache
屬性,快取的存、取操作基本都是由該屬性代理完成。
4.2.44 StrictLineReader.java
通過readLine()
函式從InputStream
中讀取一行,目前僅用於磁碟快取操作記錄檔案journal
的解析。
4.2.45 Util.java
工具類。
String readFully(Reader reader)
讀取 reader 中內容。
deleteContents(File dir)
遞迴刪除資料夾內容。
4.2.46 ContentLengthInputStream.java
InputStream
的裝飾者,可通過available()
函式得到
InputStream 對應資料來源的長度(總位元組數)。主要用於計算檔案儲存進度即圖片下載進度時的總進度。
4.2.47 FailReason.java
圖片下載及顯示時的錯誤原因,目前包括:
IO_ERROR
網路連線或是磁碟儲存錯誤。
DECODING_ERROR
decode image 為 Bitmap 時錯誤。
NETWORK_DENIED
當圖片不在快取中,且設定不允許訪問網路時的錯誤。
OUT_OF_MEMORY
記憶體溢位錯誤。
UNKNOWN
未知錯誤。
4.2.48 FlushedInputStream.java
為了解決早期 Android 版本BitmapFactory.decodeStream(…)
在慢網路情況下 decode image 異常的 Bug。
主要通過重寫FilterInputStream
的 skip(long n) 函式解決,確保 skip(long n) 始終跳過了 n 個位元組。如果返回結果即跳過的位元組數小於 n,則不斷迴圈直到
skip(long n) 跳過 n 位元組或到達檔案尾。
4.2.49 ImageScaleType.java
Image 的縮放型別,目前包括:
NONE
不縮放。
NONE_SAFE
根據需要以整數倍縮小圖片,使得其尺寸不超過 Texture 可接受最大尺寸。
IN_SAMPLE_POWER_OF_2
根據需要以 2 的 n 次冪縮小圖片,使其尺寸不超過目標大小,比較快的縮小方式。
IN_SAMPLE_INT
根據需要以整數倍縮小圖片,使其尺寸不超過目標大小。
EXACTLY
根據需要縮小圖片到寬或高有一個與目標尺寸一致。
EXACTLY_STRETCHED
根據需要縮放圖片到寬或高有一個與目標尺寸一致。
4.2.50 ViewScaleType.java
ImageAware
的 ScaleType。
將 ImageView 的 ScaleType 簡化為兩種FIT_INSIDE
和CROP
兩種。FIT_INSIDE
表示將圖片縮放到至少寬度和高度有一個小於等於
View 的對應尺寸,CROP
表示將圖片縮放到寬度和高度都大於等於 View 的對應尺寸。
4.2.51 ImageSize.java
表示圖片寬高的類。
scaleDown(…)
等比縮小寬高。
scale(…)
等比放大寬高。
4.2.52 LoadedFrom.java
圖片來源列舉類,包括網路、磁碟快取、記憶體快取。
4.2.53 ImageDecoder.java
將圖片轉換為 Bitmap 的介面,抽象函式:
Bitmap decode(ImageDecodingInfo imageDecodingInfo) throws IOException;
表示根據ImageDecodingInfo
資訊得到圖片並根據引數將其轉換為 Bitmap。
4.2.54 BaseImageDecoder.java
實現了ImageDecoder
。呼叫ImageDownloader
獲取圖片,然後根據ImageDecodingInfo
或圖片
Exif 資訊處理圖片轉換為 Bitmap。
主要函式:
(1). decode(ImageDecodingInfo decodingInfo)
呼叫ImageDownloader
獲取圖片,再呼叫defineImageSizeAndRotation(…)
函式得到圖片的相關資訊,呼叫prepareDecodingOptions(…)
得到圖片縮放的比例,呼叫BitmapFactory.decodeStream
將
InputStream 轉換為 Bitmap,最後呼叫considerExactScaleAndOrientatiton(…)
根據引數將圖片放大、翻轉、旋轉為合適的樣子返回。
(2). defineImageSizeAndRotation(InputStream imageStream, ImageDecodingInfo decodingInfo)
得到圖片真實大小以及 Exif 資訊(設定考慮 Exif 的條件下)。
(3). defineExifOrientation(String imageUri)
得到圖片 Exif 資訊中的翻轉以及旋轉角度資訊。
(4). prepareDecodingOptions(ImageSize imageSize, ImageDecodingInfo decodingInfo)
得到圖片縮放的比例。
- 如果
scaleType
等於ImageScaleType.NONE
,則縮放比例為 1; - 如果
scaleType
等於ImageScaleType.NONE_SAFE
,則縮放比例為(int)Math.ceil(Math.max((float)srcWidth / maxWidth, (float)srcHeight / maxHeight))
; - 否則,呼叫
ImageSizeUtils.computeImageSampleSize(…)
計算縮放比例。
在 computeImageSampleSize(…) 中 - 如果
viewScaleType
等於ViewScaleType.FIT_INSIDE
;
1.1 如果scaleType
等於ImageScaleType.IN_SAMPLE_POWER_OF_2
,則縮放比例從 1 開始不斷 *2 直到寬或高小於最大尺寸;
1.2 否則取寬和高分別與最大尺寸比例中較大值,即Math.max(srcWidth / targetWidth, srcHeight / targetHeight)
。 - 如果
scaleType
等於ViewScaleType.CROP
;
2.1 如果scaleType
等於ImageScaleType.IN_SAMPLE_POWER_OF_2
,則縮放比例從 1 開始不斷 *2 直到寬和高都小於最大尺寸。
2.2 否則取寬和高分別與最大尺寸比例中較小值,即Math.min(srcWidth / targetWidth, srcHeight / targetHeight)
。 - 最後判斷寬和高是否超過最大值,如果是 *2 或是 +1 縮放。
(5). considerExactScaleAndOrientatiton(Bitmap subsampledBitmap, ImageDecodingInfo decodingInfo, int rotation, boolean flipHorizontal)
根據引數將圖片放大、翻轉、旋轉為合適的樣子返回。
4.2.55 ImageDecodingInfo.java
Image Decode 需要的資訊。
String imageKey
圖片。
String imageUri
圖片 uri,可能是快取檔案的 uri。
String originalImageUri
圖片原 uri。
ImageSize targetSize
圖片的顯示尺寸。
imageScaleType
圖片的 ScaleType。
ImageDownloader downloader
圖片的下載器。
Object extraForDownloader
下載器需要的輔助資訊。
boolean considerExifParams
是否需要考慮圖片 Exif 資訊。
Options decodingOptions
圖片的解碼資訊,為 BitmapFactory.Options。
4.2.56 BitmapDisplayer.java
在ImageAware
中顯示 bitmap 物件的介面。可在實現中對 bitmap 做一些額外處理,比如加圓角、動畫效果。
4.2.57 FadeInBitmapDisplayer.java
圖片淡入方式顯示在ImageAware
中,實現了BitmapDisplayer
介面。
4.2.58 RoundedBitmapDisplayer.java
為圖片新增圓角顯示在ImageAware
中,實現了BitmapDisplayer
介面。主要通過BitmapShader
實現。
4.2.59 RoundedVignetteBitmapDisplayer.java
為圖片新增漸變效果的圓角顯示在ImageAware
中,實現了BitmapDisplayer
介面。主要通過RadialGradient
實現。
4.2.60 SimpleBitmapDisplayer.java
直接將圖片顯示在ImageAware
中,實現了BitmapDisplayer
介面。
4.2.61 BitmapProcessor.java
圖片處理介面。可用於對圖片預處理(Pre-process Bitmap)和後處理(Post-process Bitmap)。抽象函式:
public interface BitmapProcessor {
Bitmap process(Bitmap bitmap);
}
使用者可以根據自己需求去實現它。比如你想要為你的圖片新增一個水印,那麼可以自己去實現 BitmapProcessor 介面,在DisplayImageOptions
中配置 Pre-process
階段預處理圖片,這樣設定後儲存在檔案系統以及記憶體快取中的圖片都是加了水印後的。如果只希望在顯示時改變不動原圖片,可以在BitmapDisplayer
中處理。
4.2.62 PauseOnScrollListener.java
可在 View 滾動過程中暫停圖片載入的 Listener,實現了 OnScrollListener 介面。
它的好處是防止滾動中不必要的圖片載入,比如快速滾動不希望滾動中的圖片載入。在 ListView 或 GridView 中 item 載入圖片最好使用它,簡單的一行程式碼:
gridView.setOnScrollListener(new PauseOnScrollListener(ImageLoader.getInstance(), false, true));
主要的成員變數:
pauseOnScroll
觸控滑動(手指依然在螢幕上)過程中是否暫停圖片載入。
pauseOnFling
甩指滾動(手指已離開螢幕)過程中是否暫停圖片載入。
externalListener
自定義的 OnScrollListener 介面,適用於 View 原來就有自定義 OnScrollListener 情況設定。
實現原理:
重寫onScrollStateChanged(…)
函式判斷不同的狀態下暫停或繼續圖片載入。
OnScrollListener.SCROLL_STATE_IDLE
表示 View 處於空閒狀態,沒有在滾動,這時候會載入圖片。
OnScrollListener.SCROLL_STATE_TOUCH_SCROLL
表示 View 處於觸控滑動狀態,手指依然在螢幕上,通過pauseOnScroll
變數確定是否需要暫停圖片載入。這種時候大都屬於慢速滾動瀏覽狀態,所以建議繼續圖片載入。
OnScrollListener.SCROLL_STATE_FLING
表示 View 處於甩指滾動狀態,手指已離開螢幕,通過pauseOnFling
變數確定是否需要暫停圖片載入。這種時候大都屬於快速滾動狀態,所以建議暫停圖片載入以節省資源。
4.2.63 QueueProcessingType.java
任務佇列的處理型別,包括FIFO
先進先出、LIFO
後進先出。
4.2.64 LIFOLinkedBlockingDeque.java
後進先出阻塞佇列。重寫LinkedBlockingDeque
的offer(…)
函式如下:
@Override
public boolean offer(T e) {
return super.offerFirst(e);
}
讓LinkedBlockingDeque
插入總在最前,而remove()
本身始終刪除第一個元素,所以就變為了後進先出阻塞佇列。
實際一般情況只重寫offer(…)
函式是不夠的,但因為ThreadPoolExecutor
預設只用到了BlockingQueue
的offer(…)
函式,所以這種簡單重寫後做為ThreadPoolExecutor
的任務佇列沒問題。
LIFOLinkedBlockingDeque.java
包下的LinkedBlockingDeque.java
、BlockingDeque.java
、Deque.java
都是
Java 1.6 原始碼中的,這裡不做分析。
4.2.65 DiskCacheUtils.java
磁碟快取工具類,可用於查詢或刪除某個 uri 對應的磁碟快取。
4.2.66 MemoryCacheUtils.java
記憶體快取工具類。可用於根據 uri 生成記憶體快取 key,快取 key 比較,根據 uri 得到所有相關的 key 或圖片,刪除某個 uri 的記憶體快取。
generateKey(String imageUri, ImageSize targetSize)
根據 uri 生成記憶體快取 key,key 規則為[imageUri]_[width]x[height]
。
4.2.67 StorageUtils.java
得到圖片 SD 卡快取目錄路徑。
快取目錄優先選擇/Android/data/[app_package_name]/cache
;若無許可權或不可用,則選擇 App 在檔案系統的快取目錄context.getCacheDir()
;若無許可權或不可用,則選擇/data/data/[app_package_name]/cache
。
如果快取目錄選擇了/Android/data/[app_package_name]/cache
,則新建.nomedia
檔案表示不允許類似
Galley 這些應用顯示此資料夾下圖片。不過在 4.0 系統有 Bug 這種方式不生效。
4.2.68 ImageSizeUtils.java
用於計算圖片尺寸、縮放比例相關的工具類。
4.2.69 IoUtils.java
IO 相關工具類,包括 stream 拷貝,關閉等。
4.2.70 L.java
Log 工具類。
5. 雜談
聊聊 LRU
UIL 的記憶體快取預設使用了 LRU 演算法。 LRU: Least Recently Used 近期最少使用演算法, 選用了基於連結串列結構的 LinkedHashMap 作為儲存結構。
假設情景:記憶體快取設定的閾值只夠儲存兩個 bitmap 物件,當 put 第三個 bitmap 物件時,將近期最少使用的 bitmap 物件移除。
圖 1: 初始化 LinkedHashMap, 並按使用順序來排序, accessOrder = true;
圖 2: 向快取池中放入 bitmap1 和 bitmap2 兩個物件。
圖 3: 繼續放入第三個 bitmap3,根據假設情景,將會超過設定快取池閾值。
圖 4: 釋放對 bitmap1 物件的引用。
圖 5: bitmap1 物件被 GC 回收。
相關文章
- Android開源原始碼分析Android原始碼
- Universal-Image-Loader原始碼解解析---display過程 + 獲取bitmap過程原始碼
- Android 原始碼分析之 EventBus 的原始碼解析Android原始碼
- android面試——開源框架的原始碼解析Android面試框架原始碼
- React Native 0.55.4 Android 原始碼分析(Java層原始碼解析)React NativeAndroid原始碼Java
- Android 原始碼分析之 AsyncTask 原始碼分析Android原始碼
- Android Retrofit原始碼解析Android原始碼
- Android原始碼解析-LiveDataAndroid原始碼LiveData
- Android setContentView原始碼解析AndroidView原始碼
- Android Handler 原始碼解析Android原始碼
- Android——LruCache原始碼解析Android原始碼
- Android原始碼分析(LayoutInflater.from(this).inflate(resId,null);原始碼解析)Android原始碼Null
- Android 原始碼分析(一)專案構建過程Android原始碼
- Android Choreographer 原始碼分析Android原始碼
- Android 8.1 Handler 原始碼解析Android原始碼
- Android LayoutInflater Factory 原始碼解析Android原始碼
- [Android] Retrofit原始碼:流程解析Android原始碼
- 走進開源專案 - urlcat 原始碼分析原始碼
- 容器類原始碼解析系列(一) ArrayList 原始碼分析——基於最新Android9.0原始碼原始碼Android
- Android中IntentService原始碼分析AndroidIntent原始碼
- android IO Prefetch原始碼分析Android原始碼
- Android開源框架原始碼鑑賞:VirtualAPKAndroid框架原始碼APK
- Android開源框架原始碼鑑賞:EventBusAndroid框架原始碼
- PandasTA 原始碼解析(十四)AST原始碼
- Android AccessibilityService機制原始碼解析Android原始碼
- Android View 原始碼解析(一) - setContentViewAndroidView原始碼
- Android 系統原始碼-1:Android 系統啟動流程原始碼分析Android原始碼
- vue-markdown-loader原始碼解析Vue原始碼
- Android原始碼分析–ArrayMap優化Android原始碼優化
- Android Sensor原始碼分析總結Android原始碼
- Android 8.0 原始碼分析 (八) ActivityManagerServiceAndroid原始碼
- Android Jetpack系列——ViewModel原始碼分析AndroidJetpackView原始碼
- Android 網路框架 Retrofit 原始碼解析Android框架原始碼
- Android系統原始碼目錄解析Android原始碼
- weex原始碼解析(四)- android引入sdk原始碼Android
- diffusers-原始碼解析-十四-原始碼
- Android Activity Deeplink啟動來源獲取原始碼分析Android原始碼
- 下載Android單個專案原始碼的方法Android原始碼
- Android系統原始碼分析團體專案BeesAndroid正式上線啦Android原始碼