從程式碼分析Android-Universal-Image-Loader的圖片載入、顯示流程

來源:互聯網
上載者:User

標籤:

從UNIVERSAL IMAGE LOADER. PART 3(四個DisplayImage重載方法詳解)中,我們學習了Android-Universal-Image-Loader(以下簡稱UIL)中四個DisplayImage重載方法的使用,如果你還沒有學習,最好先返回去看看,不然可能不理解這篇文章。在這篇文章中我們將主要探討Android-Universal-Image-Loader的主要流程和這些流程相關的類的分析。

我們先瞭解一下UIL載入圖片的流程(可以通過查看ImageLoader.displayImage(…)方法分析得出),如

從中,我們可以看出,UIL載入圖片的一般流程是先判斷記憶體中是否有對應的Bitmap,再判斷磁碟(disk)中是否有,如果沒有就從網路中載入。最後根據原先在UIL中的配置判斷是否需要緩衝Bitmap到記憶體或磁碟中。Bitmap載入完後,就對它進行解析,然後顯示到特定的ImageView中。

有了對UIL對圖片載入和處理流程的初步認識之後,我們就可以著手分析它的原始碼了。先從ImageLoader.displayImage(...)入手,畢竟一切都因它而始。

 1     public void displayImage(String uri, ImageAware imageAware, DisplayImageOptions options, 2             ImageLoadingListener listener, ImageLoadingProgressListener progressListener) { 3         //檢查UIL的配置是否被初始化 4         checkConfiguration(); 5         if (imageAware == null) { 6             throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); 7         } 8         if (listener == null) { 9             listener = emptyListener;10         }11         if (options == null) {12             options = configuration.defaultDisplayImageOptions;13         }14 15         if (TextUtils.isEmpty(uri)) {16             engine.cancelDisplayTaskFor(imageAware);17             listener.onLoadingStarted(uri, imageAware.getWrappedView());18             if (options.shouldShowImageForEmptyUri()) {19                 imageAware.setImageDrawable(options.getImageForEmptyUri(configuration.resources));20             } else {21                 imageAware.setImageDrawable(null);22             }23             listener.onLoadingComplete(uri, imageAware.getWrappedView(), null);24             return;25         }26         //計算Bitmap的大小,以便後面解析圖片時用27         ImageSize targetSize = ImageSizeUtils.defineTargetSizeForView(imageAware, configuration.getMaxImageSize());28         String memoryCacheKey = MemoryCacheUtils.generateKey(uri, targetSize);29         engine.prepareDisplayTaskFor(imageAware, memoryCacheKey);30 31         listener.onLoadingStarted(uri, imageAware.getWrappedView());32         //Bitmap是否緩衝在記憶體?33         Bitmap bmp = configuration.memoryCache.get(memoryCacheKey);34         if (bmp != null && !bmp.isRecycled()) {35             L.d(LOG_LOAD_IMAGE_FROM_MEMORY_CACHE, memoryCacheKey);36 37             if (options.shouldPostProcess()) {38                 ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,39                         options, listener, progressListener, engine.getLockForUri(uri));40                 //處理並顯示圖片41                 ProcessAndDisplayImageTask displayTask = new ProcessAndDisplayImageTask(engine, bmp, imageLoadingInfo,42                         defineHandler(options));43                 if (options.isSyncLoading()) {44                     displayTask.run();45                 } else {46                     engine.submit(displayTask);47                 }48             } else {49                 //顯示圖片50                 options.getDisplayer().display(bmp, imageAware, LoadedFrom.MEMORY_CACHE);51                 listener.onLoadingComplete(uri, imageAware.getWrappedView(), bmp);52             }53         } else {54             if (options.shouldShowImageOnLoading()) {55                 imageAware.setImageDrawable(options.getImageOnLoading(configuration.resources));56             } else if (options.isResetViewBeforeLoading()) {57                 imageAware.setImageDrawable(null);58             }59             60             ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageAware, targetSize, memoryCacheKey,61                     options, listener, progressListener, engine.getLockForUri(uri));62             //啟動一個線程,載入並顯示圖片63             LoadAndDisplayImageTask displayTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo,64                     defineHandler(options));65             if (options.isSyncLoading()) {66                 displayTask.run();67             } else {68                 engine.submit(displayTask);69             }70         }71     }

代碼有點多,但是有很多代碼是進行異常判斷處理和函數的回調,為了先把握整體的流程,我們先放棄細節方面的追蹤。基本上重要的處理流程我都有用注釋標出。不過,從這段代碼中我們也可以看出這段代碼的結構非常清晰。對圖片的整個的載入流程都有對應的監聽介面(ImageLoadingListener.onLoadingStarted,ImageLoadingListener.onLoadingComplete,ImageLoadingListener這個類就是用來監聽圖片的載入過程的),也就是說整個的圖片載入過程程式員都可以進行相應的處理。我們先關注一片從無到有的載入過程,畢竟這部分是大家最為關心的。看到第63行中的LoadAndDisplayImageTask,跟進LoadAndDisplayImageTask.run()方法中。在這個run()方法中,除了 bmp = tryLoadBitmap();這一句是對圖片進行載入,其他的函數都是對Bitmap進行處理或者顯示。我們繼續進入看看。

 1 private Bitmap tryLoadBitmap() throws TaskCancelledException { 2         Bitmap bitmap = null; 3         try { 4             //嘗試從磁碟緩衝中讀取Bitmap 5             File imageFile = configuration.diskCache.get(uri); 6             if (imageFile != null && imageFile.exists()) { 7                 L.d(LOG_LOAD_IMAGE_FROM_DISK_CACHE, memoryCacheKey); 8                 loadedFrom = LoadedFrom.DISC_CACHE; 9 10                 checkTaskNotActual();11                 bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));12             }13             //沒有緩衝在磁碟,從網路中下載圖片14             if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {15                 L.d(LOG_LOAD_IMAGE_FROM_NETWORK, memoryCacheKey);16                 loadedFrom = LoadedFrom.NETWORK;17 18                 String imageUriForDecoding = uri;19                 if (options.isCacheOnDisk() && tryCacheImageOnDisk()) {20                     imageFile = configuration.diskCache.get(uri);21                     if (imageFile != null) {22                         imageUriForDecoding = Scheme.FILE.wrap(imageFile.getAbsolutePath());23                     }24                 }25 26                 checkTaskNotActual();27                 bitmap = decodeImage(imageUriForDecoding);28 29                 if (bitmap == null || bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) {30                     fireFailEvent(FailType.DECODING_ERROR, null);31                 }32             }33         } catch (IllegalStateException e) {34             fireFailEvent(FailType.NETWORK_DENIED, null);35         } catch (TaskCancelledException e) {36             throw e;37         } catch (IOException e) {38             L.e(e);39             fireFailEvent(FailType.IO_ERROR, e);40         } catch (OutOfMemoryError e) {41             L.e(e);42             fireFailEvent(FailType.OUT_OF_MEMORY, e);43         } catch (Throwable e) {44             L.e(e);45             fireFailEvent(FailType.UNKNOWN, e);46         }47         return bitmap;48     }

從3~12行是嘗試從磁碟緩衝中載入Bitmap。第19行判斷磁碟中是否有緩衝,就開始進行網路下載(tryCacheImageOnDisk())。在tryCacheImageOnDisk()函數中有個tryCacheImageOnDisk()的 loaded = downloadImage()這行進行圖片下載。

    private boolean downloadImage() throws IOException {        InputStream is = getDownloader().getStream(uri, options.getExtraForDownloader());        return configuration.diskCache.save(uri, is, this);    }

這個函數做的事情很簡單,就是擷取一個實現Image Downloader的downloader(當然這裡,作者根據網路情況將downloader分為慢速(slowNetworkDownloader)、正常速度(downloader)、網路拒絕(networkDeniedDownloader)情況下的download,在這裡我們不展開,你只要知道他們是imageDownloader介面的實現者就行,後面的文章會探討這個問題),然後利用Disk Cache將Bitmap寫入磁碟緩衝中。返回到之前我們進入downloadImage()函數中的tryLoadBitmap(),在將圖片緩衝到磁碟中。是否緩衝到磁碟跟配置有關)後,緊接著調用 bitmap = decodeImage(Scheme.FILE.wrap(imageFile.getAbsolutePath()));解析圖片。進入decodeImage()函數中,我們發現UIL調用Image Decoder進行圖片的解析。

1     private Bitmap decodeImage(String imageUri) throws IOException {2         ViewScaleType viewScaleType = imageAware.getScaleType();3         ImageDecodingInfo decodingInfo = new ImageDecodingInfo(memoryCacheKey, imageUri, uri, targetSize, viewScaleType,4                 getDownloader(), options);5         return decoder.decode(decodingInfo);6     }

decode()函數最終是調用BaseImageDecoder.decode()方法進行解析的,這個利用之前獲得的inputStream,直接從它身上讀取資料,然後進行解析,並對整個下載任務的網路介面進行重設。

 1 public Bitmap decode(ImageDecodingInfo decodingInfo) throws IOException { 2         Bitmap decodedBitmap; 3         ImageFileInfo imageInfo; 4  5         InputStream imageStream = getImageStream(decodingInfo); 6         try { 7             imageInfo = defineImageSizeAndRotation(imageStream, decodingInfo); 8             imageStream = resetStream(imageStream, decodingInfo); 9             Options decodingOptions = prepareDecodingOptions(imageInfo.imageSize, decodingInfo);10             decodedBitmap = BitmapFactory.decodeStream(imageStream, null, decodingOptions);11         } finally {12             IoUtils.closeSilently(imageStream);13         }14 15         if (decodedBitmap == null) {16             L.e(ERROR_CANT_DECODE_IMAGE, decodingInfo.getImageKey());17         } else {18             decodedBitmap = considerExactScaleAndOrientatiton(decodedBitmap, decodingInfo, imageInfo.exif.rotation,19                     imageInfo.exif.flipHorizontal);20         }21         return decodedBitmap;22     }

接下來,有瞭解析好的Bitmap對象後,剩下的就是在Image View對象中顯示它了。我們回到文章一開始介紹到的ImageLoader.displayImage(...)函數中(相關的代碼在文章的開頭處可以看到)。

為了方便,我還是將ImageLoader.displayImage(...)中涉及的代碼貼在下面。

1         DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine, loadedFrom);2         runTask(displayBitmapTask, syncLoading, handler, engine);

我們進去DisplayBitmapTask.run()函數中看看。除去前面幾行的ImageLoadingListener.ImageLoadingListener()代碼,相關代碼其實就一行 displayer.display(bitmap, imageAware, loadedFrom),它其實就是調用BitmapDisplayer這個對象將Bitmap對象顯示到ImageView上。根據實現BitmapDisplayer介面的不同對象,還有SimpleBitmapDisplayer、FadeInBitmapDisplayer、RoundedBitmapDisplayer、RoundedVignetteBitmapDisplayer這5種對象。

 

最後,讓我們用任務流圖概況以上的處理流程中對應介面。

在接下去的文章中,我們會介紹UIL中的包設計、緩衝、下載、多任務機制。

從程式碼分析Android-Universal-Image-Loader的圖片載入、顯示流程

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.