編輯:關於android開發
關於使用配置請看Android-Universal-Image-Loader (圖片異步加載緩存庫)的使用配置
前面介紹了如何在我們的項目中使用Android-Universal-Image-Loader,本文看一下UIL的工作過程。
在看之前我們先看一下官方的這張圖片,它代表著所有條件下的執行流程:

圖片給出的加載過程分別對應三種情況:
1.當內存中有該 bitmap 時,直接顯示。
2.當本地有該圖片時,加載進內存,然後顯示。
3. 內存本地都沒有時,請求網絡,下載到本地,接下來加載進內存,然後顯示。
過程分析:
最終展示圖片還是調用的ImageLoader 這個類中的 display() 方法,那麼我們就把注意力集中到ImageLoader 這個類上,看下display()內部怎麼實現的。
public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options,
ImageLoadingListener listener, ImageLoadingProgressListener progressListener) {
// 首先檢查初始化配置,configuration == null 拋出異常
checkConfiguration();
if (imageAware == null) {
throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS);
}
if (listener == null) {
listener = defaultListener;
}
if (options == null) {
options = configuration.defaultDisplayImageOptions;
}
// 當 目標uri "" 時這種情況的處理
if (TextUtils.isEmpty(uri)) {
engine.cancelDisplayTaskFor(imageAware);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
if (options.shouldShowImageForEmptyUri()) {
imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));
} else {
imageAware.setImageDrawable(null);
}
listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);
return;
}
// 根據 配置的大小與圖片實際大小得出 圖片尺寸
ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());
String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);
engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);
listener.onLoadingStarted(uri, imageAware.getWrappedView());
// 首先從內存中取,看是否加載過,有緩存直接用
Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp != null && !bmp.isRecycled()) {
L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);
if (options.shouldPostProcess()) {
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
} else {
options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);
listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);
}
} else {
// 沒有緩存
if (options.shouldShowImageOnLoading()) {
imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));
} else if (options.isResetViewBeforeLoading()) {
imageAware.setImageDrawable(null);
}
ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,
options, listener, progressListener, engine.getLockForUri(uri));
LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,
defineHandler(options));
if (options.isSyncLoading()) {
displayTask.run();
} else {
engine.submit(displayTask);
}
}
}
首先判斷傳入的目標url 是" ",如果空,是否配置了默認的圖片,接下來重點在url 是合法的情況下,去加載bitmap,首先從內存中去取,看能否取到(如果前面加載到內存,並且緩存過,沒有被移除,則可以取到),如果取到則直接展示就可以了。
如果沒有在內存中取到,接下來執行LoadAndDisplayImageTask 這個任務,主要還是看run()方法的執行過程:
@Override
public void run() {
if (waitIfPaused()) return;
if (delayIfNeed()) return;
ReentrantLock loadFromUriLock = imageLoadingInfo.loadFromUriLock;
L.d(LOG_START_DISPLAY_IMAGE_TASK, memoryCacheKey);
if (loadFromUriLock.isLocked()) {
L.d(LOG_WAITING_FOR_IMAGE_LOADED, memoryCacheKey);
}
loadFromUriLock.lock();
Bitmap bmp;
try {
checkTaskNotActual();
bmp = configuration.memoryCache.get(memoryCacheKey);
if (bmp == null || bmp.isRecycled()) {
// cache 中沒有,下載
bmp = tryLoadBitmap();
if (bmp == null) return; // listener callback already was fired
checkTaskNotActual();
checkTaskInterrupted();
if (options.shouldPreProcess()) {
L.d(LOG_PREPROCESS_IMAGE, memoryCacheKey);
bmp = options.getPreProcessor().process(bmp);
if (bmp == null) {
L.e(ERROR_PRE_PROCESSOR_NULL, memoryCacheKey);
}
}
// 加入到內存的緩存
if (bmp != null && options.isCacheInMemory()) {
L.d(LOG_CACHE_IMAGE_IN_MEMORY, memoryCacheKey);
//LruMemoryCache
configuration.memoryCache.put(memoryCacheKey, bmp);
}
} else {
loadedFrom = LoadedFrom.MEMORY_CACHE;
L.d(LOG_GET_IMAGE_FROM_MEMORY_CACHE_AFTER_WAITING, memoryCacheKey);
}
if (bmp != null && options.shouldPostProcess()) {
L.d(LOG_POSTPROCESS_IMAGE, memoryCacheKey);
bmp = options.getPostProcessor().process(bmp);
if (bmp == null) {
L.e(ERROR_POST_PROCESSOR_NULL, memoryCacheKey);
}
}
checkTaskNotActual();
checkTaskInterrupted();
} catch (TaskCancelledException e) {
fireCancelEvent();
return;
} finally {
loadFromUriLock.unlock();
}
DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);
runTask(displayBitmapTask, syncLoading, handler, engine);
}
接下來,因為設置到了下載讀寫等過程,所以加了 鎖,保證線程安全,下載過程是在 上面的// cache 中沒有,這個注釋下面的tryLoadBitmap() 方法中進行的,這個方法中做了什麼,我們一會在看,現在繼續往下走,下載後拿到了bitmap,接著進行判斷是否 把bitmap加入到內存中的緩存中。 最後在DisplayBitmapTask 的run 方法中setImageBitmap設置為背景。
這就是大體工作流程,也是前面說的的 三種情況
1. 內存中有,直接顯示。
2. 內存中沒有 本地有,加載進內存並顯示。
3 本地沒有,網絡下載,本地保存,加載進內存,顯示。
接下來再看前面說的下載方法tryLoadBitmap(), 由於比較長,這裡只看關鍵的 try代碼塊中的操作:
// 嘗試 本地文件中是否有緩存
File imageFile = configuration.diskCache.get(uri);
if (imageFile != null && imageFile.exists() && imageFile.length() > 0) {
L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey);
loadedFrom = LoadedFrom.DISC_CACHE;
checkTaskNotActual();
bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));
}
// 本地也沒有
if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {
L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);
loadedFrom = LoadedFrom.NETWORK;
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);
如果沒有則 下載,首先判斷是否允許保存到本地,如果允許則下載到本地,接下來通過bitmap = decodeImage(imageUriForDecoding);拿到目標bitmap ,並返回 用於顯示。
那麼我們接下來看一下UIL 是采取哪些方式去緩存內存和本地文件的。
通過查看UIL的lib庫我們可以看出,整個lib 主要有三個包組成

①cache:管理緩存 ②core:下載的核心 ③utils:一些輔助工具。
utils 不用管,剩下的兩部分就是整個項目的精髓: 下載展示 和緩存。
我們這裡先看一下cache:

也是由兩部分組成:磁盤和內存。
disc 有兩種cache類型:

第一類是是基於DiskLruCache的LruDiskCache, 第二類是基於BaseDiskCache的LimitedAgeDiskCache 和UnlimitedDiskCache 。
這兩種的工作原理稍微復雜,在這裡先不做介紹,有時間單獨再專門開篇文章介紹。
這兩種的相同點是都是將請求到的圖片 inputStream寫到本地文件中。不同點在魚管理方式不同,
LruDiskCache是根據size > maxSize || fileCount > maxFileCount || 或者存的數據超過2000條而自動去刪除。
LimitedAgeDiskCache 是根據存入時間與當前時間差,是否大於過期時間 去判斷是從新下載 還是重復利用。
UnlimitedDiskCache:這個就是不限制cache大小,只要disk 上有空間 就可以保存到本地。
以上三個都實現了DiskCache 這個接口,具體工作過程是 save get remove clear 等幾個方法,類似於數據庫的 curd 操作。
memory的緩存 的實現類比較多,都是實現了 MemoryCache 這個接口
public interface MemoryCache {
/**
* Puts value into cache by key
* 根據Key將Value添加進緩存中
* @return true - if value was put into cache successfully, false - if value was not put into
* cache
*/
boolean put(String key, Bitmap value);
/** Returns value by key. If there is no value for key then null will be returned. */
根據Key 取Value
Bitmap get(String key);
/** Removes item by key */
根據Key移除對應的Value
Bitmap remove(String key);
/** Returns all keys of cache */
返回所有的緩存Keys
Collection keys();
/** Remove all items from cache */
情況緩存的map
void clear();
}
@Override
public boolean put(String key, Bitmap value) {
boolean putSuccessfully = false;
// Try to add value to hard cache
// 當前要存入的 size
int valueSize = getSize(value);
// 約定的最大size
int sizeLimit = getSizeLimit();
//當前存在的size 大小
int curCacheSize = cacheSize.get();
//如果當前沒有滿,存入
if (valueSize < sizeLimit) {
// 判斷 存入後如果 超出了約定的 maxsize 則刪除掉最早的那一條
while (curCacheSize + valueSize > sizeLimit) {
Bitmap removedValue = removeNext();
if (hardCache.remove(removedValue)) {
curCacheSize = cacheSize.addAndGet(-getSize(removedValue));
}
}
hardCache.add(value);
cacheSize.addAndGet(valueSize);
putSuccessfully = true;
}
// 如果過大,則不存入到上面的集合,則將value 先new WeakReference(value)中,然後在加入Map 中
// Add value to soft cache
super.put(key, value);
return putSuccessfully;
}
注釋的很詳細, 首先判斷大小可以加入List
謝謝認真觀讀本文的每一位小伙伴,衷心歡迎小伙伴給我指出文中的錯誤,也歡迎小伙伴與我交流學習。
注釋設置,eclipse設置注釋模板
注釋設置,eclipse設置注釋模板自動添加上一些關於文件開頭的注釋信息: 增加函數注釋模板: 注意:先創建 Template Group 再創建 Li
仿有道詞典應用項目源碼,有道詞典項目源碼
仿有道詞典應用項目源碼,有道詞典項目源碼主要學習viewPage用線程的自動切換,訊飛語音應用,百度翻譯API,上拉加載下拉刷新,listView與ScrollView結
Android屬性動畫
Android屬性動畫 屬性動畫系統是一個健壯 的框架,它幾乎可以允許把任何對象變成動畫。可以根據時間的推移來改變任何對象的屬性來定義一個動畫,而不用關心該對象是否要繪制
ORB_SLAM2在Android上的移植過程
ORB_SLAM2在Android上的移植過程 一直沒時間寫博客,最近抽時間寫了些關於在ORB_SLAM2在Android上的移植過程,也算是點經驗吧。 寫完後一個手