編輯:關於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 操作。
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 Collectionkeys(); /** 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注釋的很詳細, 首先判斷大小可以加入List(value)中,然後在加入Map 中 // Add value to soft cache super.put(key, value); return putSuccessfully; }
注釋設置,eclipse設置注釋模板自動添加上一些關於文件開頭的注釋信息: 增加函數注釋模板: 注意:先創建 Template Group 再創建 Li
仿有道詞典應用項目源碼,有道詞典項目源碼主要學習viewPage用線程的自動切換,訊飛語音應用,百度翻譯API,上拉加載下拉刷新,listView與ScrollView結
Android屬性動畫 屬性動畫系統是一個健壯 的框架,它幾乎可以允許把任何對象變成動畫。可以根據時間的推移來改變任何對象的屬性來定義一個動畫,而不用關心該對象是否要繪制
ORB_SLAM2在Android上的移植過程 一直沒時間寫博客,最近抽時間寫了些關於在ORB_SLAM2在Android上的移植過程,也算是點經驗吧。 寫完後一個手