Android圖片加載庫的理解

前言   

    這是「基礎自測」系列的第三篇文章,以Android開發須要熟悉的20個技術點爲切入點,本篇重點講講Android中的ImageLoader這個庫的一些理解,在Android上最讓人頭疼是從網絡中獲取圖片,顯示,回收,任何一個環節有問題均可能直接OOM,當須要加載大量的圖片的時候,每當快速滑,有時候會很卡,甚至會由於內存溢出而崩潰。這裏講解的庫是: Universal_Image_Loader

內容目錄

  • ImageLoader設計原理
  • ImageLoader流程圖
  • ImageLoader的使用
  • ImageLoader優化
  • Fresco介紹
  • 圖片策略優化
  • 小結

ImageLoader設計原理

ImageLoader的工做原理:在顯示圖片的時,它會先在內存中查找,若是沒有就去本地查找,若是尚未,就開一個新的線程去下載這張圖片,下載成功會把圖片同時緩存在內存和本地。html

咱們在基於這個原理,在每次退出一個頁面時候,把ImageLoader內存中緩存所有清除,這樣就節省了大量內存,反正下次再用到的時候從本地再取出來就好了。須要說明的是,因爲ImageLoader對圖片是軟引用的形式,因此在內存中的圖片會存在內存不足的時候被系統回收。
總設計圖:

ImageLoader流程圖

ImageLoader的使用

ImageLoader由三大組件組成:
  1. ImagaLoaderConfiguaration——對圖片緩存進行整體配置,包含內存緩存的大小,本地緩存的大小和位置、日誌、下載策略(FIFO仍是LIFO)等。
  2. ImageLoader——通常使用displayImage來把URL對應的圖片顯示在ImageView上。
  3. DisplayImageOptions——在每一個頁面須要顯示圖片的地方,控制如何顯示的細節,好比指定下載時的默認圖、是否將緩存放到內存或本地磁盤。

從三者的協做關係上看,他們有點像廚房規定、廚師、客戶我的口味之間的關係。ImageLoaderConfiguration就像是廚房裏面的規定,每個廚師要怎麼着裝,要怎麼保持廚房的乾淨,這是針對每個廚師都適用的規定,並且不容許個性化改變。ImageLoader就像是具體作菜的廚師,負責具體菜譜的製做。DisplayImageOptions就像每一個客戶的偏好,根據客戶是重口味仍是清淡,每個imageLoader根據DisplayImageOptions的要求具體執行。android

ImagaLoaderConfiguaration
示例代碼:
// DON'T COPY THIS CODE TO YOUR PROJECT! This is just example of ALL options using.// See the sample project how to use ImageLoader correctly.File cacheDir =StorageUtils.getCacheDirectory(context);
ImageLoaderConfiguration config =newImageLoaderConfiguration.Builder(context)
        .memoryCacheExtraOptions(480, 800) // default = device screen dimensions
        .diskCacheExtraOptions(480, 800, null)
        .taskExecutor(...)
        .taskExecutorForCachedImages(...)
        .threadPoolSize(3) // default
        .threadPriority(Thread.NORM_PRIORITY-2) // default
        .tasksProcessingOrder(QueueProcessingType.FIFO) // default
        .denyCacheImageMultipleSizesInMemory()
        .memoryCache(newLruMemoryCache(2*1024*1024))
        .memoryCacheSize(2*1024*1024)
        .memoryCacheSizePercentage(13) // default
        .diskCache(newUnlimitedDiskCache(cacheDir)) // default
        .diskCacheSize(50*1024*1024)
        .diskCacheFileCount(100)
        .diskCacheFileNameGenerator(newHashCodeFileNameGenerator()) // default
        .imageDownloader(newBaseImageDownloader(context)) // default
        .imageDecoder(newBaseImageDecoder()) // default
        .defaultDisplayImageOptions(DisplayImageOptions.createSimple()) // default
        .writeDebugLogs()
        .build();
能夠看到ImagaLoaderConfiguaration的職責就是記錄相關的配置,它的內部就是一些字段的集合。
DisplayImageOptions

每個ImageLoader.displayImage(...)均可以使用DisplayImageOptions,具體配置代碼以下:git

// DON'T COPY THIS CODE TO YOUR PROJECT! This is just example of ALL options using.
// See the sample project how to use ImageLoader correctly.
DisplayImageOptions options = new DisplayImageOptions.Builder()
        .showImageOnLoading(R.drawable.ic_stub) // resource or drawable
        .showImageForEmptyUri(R.drawable.ic_empty) // resource or drawable
        .showImageOnFail(R.drawable.ic_error) // resource or drawable
        .resetViewBeforeLoading(false)  // default
        .delayBeforeLoading(1000)
        .cacheInMemory(false) // default
        .cacheOnDisk(false) // default
        .preProcessor(...)
        .postProcessor(...)
        .extraForDownloader(...)
        .considerExifParams(false) // default
        .imageScaleType(ImageScaleType.IN_SAMPLE_POWER_OF_2) // default
        .bitmapConfig(Bitmap.Config.ARGB_8888) // default
        .decodingOptions(...)
        .displayer(new SimpleBitmapDisplayer()) // default
        .handler(new Handler()) // default
        .build();
ImageLoader
最終使用時候,簡單幾句就好了,傳入一個ImageView控件
ImageLoader imageLoader = ImageLoader.getInstance(); // Get singleton instance
// Load image, decode it to Bitmap and display Bitmap in ImageView (or any other view 
//  which implements ImageAware interface)
imageLoader.displayImage(imageUri, imageView);
又或者直接
// Load image, decode it to Bitmap and return Bitmap to callback
imageLoader.loadImage(imageUri, new SimpleImageLoadingListener() {
    @Override
    public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) {
        // Do whatever you want with Bitmap
    }
});

ImageLoader優化

儘管ImageLoader很強大,但一直把圖片緩存在內存中,會致使內存佔用太高。雖然對圖片的引用是軟引用,軟引用在內存不夠的時候會被GC,但咱們仍是但願減小GC的次數,因此要常常手動清理ImageLoader中的緩存。
好比,咱們常常在作項目的時候,會有一個AppBaseActivity的基類,這樣咱們能夠在基類的onDestroy方法中,執行ImageLoader的clearMemoryCache方法,以確保頁面銷燬時,把爲了顯示這個頁面而增長的內存緩存清除,這樣即便到了下個頁面要複用以前加載過的圖片,雖然內存沒有了,根據ImageLoader的緩存策略,在本地磁盤上仍是能被找到的。
好比:
 
protected void onDestroy() {
        //回收該頁面緩存在內存的圖片
        imageLoader.clearMemoryCache();
 
        super.onDestroy();
}

Fresco介紹

簡介:

Fresco 是一個強大的圖片加載組件。它是Facebook開源的圖片加載庫github

Fresco 中設計有一個叫作 image pipeline 的模塊。它負責從網絡,從本地文件系統,本地資源加載圖片。爲了最大限度節省空間和CPU時間,它含有3級緩存設計(2級內存,1級文件)。緩存

Fresco 中設計有一個叫作 Drawees 模塊,方便地顯示loading圖,當圖片再也不顯示在屏幕上時,及時地釋放內存和空間佔用。性能優化

Fresco 支持 Android2.3(API level 9) 及其以上系統。服務器

 

流程圖:
 
 
原理:
Fresco 設計了image pipeline 的概念,它負責前後檢查內存,磁盤文件,若是都沒有再老老實實從網絡下載圖片。
主要有個三層緩存的概念
第一層:Bitmap緩存
在Android 5.0系統中,考慮到內存管理有了很大改進,因此 Bitmap緩存位於Java的heap中,而在Android 4.X或更低的系統中,Bitmap緩存位於ashmem中,而不是位於Java的heap中,這意味着圖片的建立和回收不會引起過多的GC,從而讓APP運行的更快。
第二層:內存緩存
內存緩存中存儲了圖片的原始壓縮格式,從內存緩存中取出的圖片,在顯示前必須先解碼,當APP切換到後臺時,內存緩存也會被清空。
第三層:磁盤緩存
又名本地緩存,磁盤緩存中存儲的也是圖片的原始壓縮格式,在使用前也要先解碼,當APP切換到後臺時,磁盤緩存不會丟失,即便關機也不會。
 
使用:

爲了下載網絡圖片,請確保在 AndroidManifest.xml 中有如下權限:網絡

<uses-permission android:name="android.permission.INTERNET"/>

在 Application 初始化時,在應用調用 setContentView() 以前,進行初始化:多線程

Fresco.initialize(context); 

在xml佈局文件中, 加入命名空間:框架

<!-- 其餘元素 -->
<LinearLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:fresco="http://schemas.android.com/apk/res-auto">
加入SimpleDraweeView:

<com.facebook.drawee.view.SimpleDraweeView
    android:id="@+id/my_image_view"
    android:layout_width="20dp"
    android:layout_height="20dp"
    fresco:placeholderImage="@drawable/my_drawable"
  />

開始加載圖片

Uri uri = Uri.parse("https://raw.githubusercontent.com/facebook/fresco/gh-pages/static/fresco-logo.png");
SimpleDraweeView draweeView = (SimpleDraweeView) findViewById(R.id.my_image_view);
draweeView.setImageURI(uri);

圖片策略優化

咱們這裏說的圖片,是根據服務端的接口返回的圖片URL地址開啓一個線程下載到APP本地並顯示的,不少APP崩潰的緣由就是圖片的問題沒處理好。那麼就有一些相關解決方案策略,以下。
  1. 要確保下載的每張圖,都符合ImageView控件的大小。能夠事先準備不少套不一樣的分辨率的圖片,而後每次根據URL請求圖片時,都要額外在URL上加兩個參數,width和height,從而要求服務器返回其中某一個張圖,好比:http://www.aaa.com/a.png?width=100&height=50
  2. 低流量模式。在2G和3G網絡環境下,咱們應該適當下降圖片的質量,下降圖片質量,相應的圖片大小也會下降,簡稱低流量模式,好比在請求網址中再添加一個參數,叫作quality,在2G網絡下這個值爲50%,在3G網絡狀況下,這個值爲70%,這樣就會將JPG圖片質量下降爲50%或70%。
  3. 極速模式。發如今2G和3G網絡環境下,大部分用戶對圖片不感興趣,咱們能夠設計一些只有文字的頁面,這種頁面稱呼爲極速模式,以節省流量。

小結

本篇主要介紹了第三方圖片加載庫的原理和使用方法,特別是ImageLoader的介紹,由於圖片顯示在APP中是很常見的應用場景,何況ImageLoader已經封裝好了一些類和方法。咱們能夠直接拿來用了。而不用重複去寫了。若是本身去寫一個的話,這方面的程序仍是比較麻煩的,要考慮多線程緩存,內存溢出等不少方面,再說這麼多程序都在用,說明穩定性仍是可靠的,相似這種工具類能用穩定成熟的,咱們做爲一名開發人員,專一度仍是在應用業務開發上。

閱讀擴展

源於對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中能夠看到技術積累的過程。
相關文章
相關標籤/搜索