1. 程式人生 > >Android平滑圖片載入和快取庫 Glide 使用詳解

Android平滑圖片載入和快取庫 Glide 使用詳解

一、簡介

在泰國舉行的谷歌開發者論壇上,谷歌為我們介紹了一個名叫 Glide的圖片載入庫,作者是bumptech。這個庫被廣泛的運用在google的開源專案中,包括2014google I/O大會上釋出的官方app

二、 使用

dependencies {

    compile 'com.github.bumptech.glide:glide:3.6.0'

    compile 'com.android.support:support-v4:23.3.0’

}

 如何檢視最新版本

  http://search.maven.org/#search%7Cga%7C1%7Ca%3A%22glide%22

三 、使用方法及詳細介紹

     http://mrfu.me/2016/02/27/Glide_Getting_Started/  介紹的很詳細,強烈建議看看。

Yelp app是如何使用Glide優化圖片載入的

原文:Glide – How Yelp’s Android App Loads Images 

動態載入圖片是很多安卓應用的基礎。在Yelp(美國最大點評網站)中,圖片在把消費者與商家聯絡起來的過程中至關重要。隨著網路通訊和硬體水平的越發強大,消費者對於圖片數量和圖片質量的期望日益增長。圖片可以輕易的成為記憶體和網路流量的消耗大戶,處理圖片資料的下載和管理成為了一個讓人望而卻步的任務。我們探索了幾種處理這個問題的解決辦法,最終認為Glide

在效能,使用方便性,穩定性上達到了相當好的平衡。

Glide最簡單的使用案例就是從遠端伺服器或者本地檔案系統載入圖片,把它們放在磁碟與記憶體快取中,然後載入到view上。它可以用在全市圖片的app中,Glide為包含圖片的滾動列表做了儘可能流暢的優化。

物件池

Glide原理的核心是為bitmap維護一個物件池。物件池的主要目的是通過減少大物件的分配以重用來提高效能(至於物件池的概覽,可以檢視這個Android performance pattern視訊)。

DalvikART虛擬機器都沒有使用compacting garbage collectorcompacting garbage collector

是一種模式,這種模式中GC會遍歷堆,同時把活躍物件移到相鄰記憶體區域,讓更大的記憶體塊可以用在後續的分配中。因為安卓沒有這種模式,就可能會出現被分配的物件分散在各處,物件之間只有很小的記憶體可用。如果應用試圖分配一個大於鄰近的閒置記憶體塊空間的物件,就會導致OutOfMemoryError,然後崩潰,即使總的空餘記憶體空間大於物件的大小。

使用物件池還可以幫助提高滾動的效能,因為重用bitmap意味著更少的物件被建立與回收。垃圾回收會導致停止一切(Stop The World)”事件,這個事件指的是回收器執行期間,所有執行緒(包括UI執行緒)都會暫停。這個時候,影象幀無法被渲染同時UI可能會停滯,這在滾動期間尤其明顯。

scrolling-slight

Glide的使用

Glide使用起來很簡單,而且不需要任何特別的配置就自動包含了bitmap pooling

DrawableRequestBuilder requestBuilder = Glide.with(context).load(imageUrl);
requestBuilder.into(imageView);


這就是載入一張圖片的全部要求。就像安卓中的很多地方一樣,with()方法中的context到底是哪種型別是不清楚的。有一點很重要需要記住,就是傳入的context型別影響到Glide載入圖片的優化程度,Glide可以監視activity的生命週期,在activity銷燬的時候自動取消等待中的請求。但是如果你使用Application context,你就失去了這種優化效果。

譯者注:其實以上的程式碼是一種比較規範的寫法,我們更熟悉的寫法是:

Glide.with(context)
    .load("http://inthecheesefactory.com/uploads/source/glidepicasso/cover.jpg")
    .into(ivImg);

優化特性

類似的是,如果相關的item已經滾出了螢幕的範圍,Glide會自動取消列表中的懸著的圖片請求。因為絕大多數開發者都會在adapter中利用view的回收,Glide做到這點是通過在ImageView上設定一個tag,在載入另外一張圖片之前檢查這個tag,如果存在就取消第一次請求。

Glide提供了幾個讓你感覺圖片載入速度變快的特性。第一個就是在圖片顯示在螢幕上之前就預先取出圖片。它提供了一個ListPreloader類,它被應該事先取出的item數目例項化。然後通過setOnScrollListener(OnScrollListener).被傳遞給ListView。你想在ListView之外也能預先取出圖片嗎?沒問題,使用前面的builder物件就可以了,只需呼叫builder.downloadOnly()

downloadOnly見:https://github.com/bumptech/glide/wiki/Loading-and-Caching-on-Background-Threads

我們發現了Glide提供的可以大大提高效能,穩定性的功能,以及安卓圖片載入領域的一些設計哲學。這些特性和優化確實可以很好的將圖片載入的體驗變成一種享受。

Glide:快速和高效的Android平臺多媒體資源管理庫

Glide 是一個android平臺上的快速和高效的開源的多媒體資源管理庫,提供多媒體檔案的壓縮,記憶體和磁碟快取,資源池的介面

glide_logo

Glide 支援獲取,解壓展示視訊,影象和GIFs,  Glide有一個可彈性的api可以讓開發者自定義網路棧技術,預設使用HttpUrlConnection ,你可以替換為  Google’s Volley或者 OkHttp

Glide 開始的目的是提供一個快速和平滑展示圖片列表,但是Glide對你需要拉取, resize和展示遠端的圖片這些場景都是很管用的.

使用

參考這裡 GitHub wiki和這裡 javadocs.

簡單的例子

// For a simple view:
@Override
public void onCreate(Bundle savedInstanceState) {
    ...
 
    ImageView imageView = (ImageView) findViewById(R.id.my_image_view);
 
    Glide.with(this).load("http://goo.gl/h8qOq7").into(imageView);
}
 
// For a list:
@Override
public View getView(int position, View recycled, ViewGroup container) {
    final ImageView myImageView;
    if (recycled == null) {
        myImageView = (ImageView) inflater.inflate(R.layout.my_image_view,
                container, false);
    } else {
        myImageView = (ImageView) recycled;
    }
 
    String url = myUrls.get(position);
 
    Glide.with(myFragment)
        .load(url)
        .centerCrop()
        .placeholder(R.drawable.loading_spinner)
        .crossFade()
        .into(myImageView);
 
    return myImageView;
}


Volley

如果你想使用Volley

Gradle

dependencies{

    compile'com.github.bumptech.glide:volley-integration:1.0.+'

    compile'com.mcxiaoke.volley:library:1.0.+'

}

然後在你的Activity或者程式中,註冊Volley為基本模組

public void onCreate() {
  Glide.get(this).register(GlideUrl.class, InputStream.class,
        new VolleyUrlLoader.Factory(yourRequestQueue));
  ...
}


OkHttp

Gradle:

dependencies{

    compile'com.github.bumptech.glide:okhttp-integration:1.0.+'

    compile'com.squareup.okhttp:okhttp:2.0.+'

}

然後在你的Activity或者程式中,註冊OkHttp為基本模組

public void onCreate() {
  Glide.get(this).register(GlideUrl.class, InputStream.class,
        new OkHttpUrlLoader.Factory(yourOkHttpClient));
  ...
}

四、圖片的快取和快取的時效機制

1.圖片快取的鍵值

圖片快取的鍵值主要用於DiskCacheStrategy.RESULT,Glide當中的鍵值主要包含三個部分:

通過DataFetcher.getId()方法返回的String資料作為鍵值。一般的DataFetchers會簡單返回資料模型data model的toString()結果,如果是URL/File會返回相應的路徑

圖片的尺寸,主要是通過override(width,height)或者通過Target's getSize()方法確定的尺寸資訊

包含一個可選的簽名所有的這些東西會通過一種雜湊演算法生成一個獨有、安全的檔名,通過此檔名將圖片快取在disk中

2.快取失效

因為Glide當中圖片快取key的生成是通過一個雜湊演算法來實現的,所以很難手動去確定哪些檔案可以從快取當中進行刪除

2.1 當內容(url,file path)改變的時候,改變相應的識別符號就可以了,Glide當中也提供了signature()方法,將一個附加的資料加入到快取key當中

多媒體儲存資料,可用MediaStoreSignature類作為識別符號,會將檔案的修改時間、mimeType等資訊作為cacheKey的一部分

檔案,使用StringSignature

Urls ,使用StringSignature

Glide.with(yourFragment)
 .load(yourFileDataModel)
 .signature(new StringSignature(yourVersionMetadata))
 .into(yourImageView);
 
 
Glide.with(fragment)
 .load(mediaStoreUri)
 .signature(new MediaStoreSignature(mimeType, dateModified, orientation))
 .into(view);

自定義識別符號:
public class IntegerVersionSignature implements Key {
  private int currentVersion;
  public IntegerVersionSignature(int currentVersion) {
     this.currentVersion = currentVersion;
  }
  @Override
  public boolean equals(Object o) {
    if (o instanceof IntegerVersionSignature) {
      IntegerVersionSignature other = (IntegerVersionSignature) o;
      return currentVersion = other.currentVersion;
    }
    return false;
  }
  @Override
  public int hashCode() {
    return currentVersion;
  }
  @Override
  public void updateDiskCacheKey(MessageDigest md) {
    messageDigest.update(ByteBuffer.allocate(Integer.SIZE)
.putInt(signature).array());
  }
}

2.2、不快取可以通過diskCacheStrategy(DiskCacheStrategy.NONE.)實現

五、配置GlideModules

可以通過GlideModule介面來配置Glide的配置檔案,並且像ModelLoaders一樣註冊相關元件。

包含一個GlideMode :

第一步、To use and register a GlideModule, first implement the interface with your configuration and components:

public class MyGlideModule implements GlideModule {
  @Override
  public void applyOptions(Context context, GlideBuilder builder) {
    // Apply options to the builder here.
  }
  @Override
  public void registerComponents(Context context, Glide glide) {
    // register ModelLoaders here.
  }
}

第二步、然後將上面的實現了加入到proguard.cfg當中:
-keepnames class * com.mypackage.MyGlideModule

第三步、在AndroidManifest.xml檔案中新增meta-data,以便Glide能夠找到你的Module
<meta-data
android:name="com.bumptech.glide.samples.flickr.FlickrGlideModule"
 android:value="GlideModule" />

六、Library專案

一個Library專案可能會定義一個或者多個GlideModules,如果一個Library專案新增一個Module到Library專案的manifest當中,依賴於此Library的應用就會自動載入依賴庫(Library專案)當中的Module。

當然,如果manifest的合併不正確,那麼Library裡面Module就必須手動地在應用當中新增進去。

七、GlideModules衝突

雖然Glide允許一個應用當中存在多個GlideModules,Glide並不會按照一個特殊的順序去呼叫已註冊的GlideModules,如果一個應用的多個依賴工程當中有多個相同的Modules,就有可能會產生衝突。

如果一個衝突是不可避免的,應用應該預設去定義一個自己的Module,用來手動地處理這個衝突,在進行Manifest合併的時候,可以用下面的標籤排除衝突的module。

八、通過GlideBuilder配置全域性配置檔案

Glide允許開發者配置自定義的全域性操作應用於所有的請求,這個部分可以通過GlideModule介面中的applyOptions方法的GlideBuilder引數實現 :

1.DiskCache

1.1、硬碟快取是在一個後臺執行緒當中,通過一個DiskCache.Factory介面進行快取的。

開發者能夠通過GlideBuilder的setDiskCache(DiskCache.Factory df)方法設定儲存的位置和大小

通過傳入DiskCacheAdapter來完全禁用快取

自定義一個DiskCache來完全禁用快取,

Glide預設是用InternalCacheDiskCacheFactory類來建立硬碟快取的,這個類會在應用的內部快取目錄下面建立一個最大容量250MB的快取資料夾,使用這個快取目錄而不用sd卡,意味著除了本應用之外,其他應用是不能訪問快取的圖片檔案的。

1.2.設定disk快取的大小 : InternalCacheDiskCacheFactory

builder.setDiskCache(new InternalCacheDiskCacheFactory(context, yourSizeInBytes));

1.3.設定快取的路徑

可以通過實現DiskCache.Factory,然後使用DiskLruCacheWrapper建立一個新的快取目錄,比如,可以通過如下方式在外存當中建立快取目錄:

builder .setDiskCache(new DiskCache.Factory() {
  @Override
  public DiskCache build() { 
    // Careful: the external cache directory doesn't enforce permissions
    File cacheLocation = new File(context.getExternalCacheDir(), "cache_dir_name");
    cacheLocation.mkdirs();
    return DiskLruCacheWrapper.get(cacheLocation, yourSizeInBytes);
  }
});

2.記憶體當中的快取和POOLS

GlideBuilder當中,允許開發者去設定記憶體當中圖片快取區的大小,主要涉及到的類包括MemoryCache和BitmapPool

2.1 大小的設定

預設記憶體快取的大小是用過MemorySizeCalculator來實現的,這個類會根據裝置螢幕的大小,計算出一個合適的size,開發者可以獲取到相關的預設設定資訊:

MemorySizeCalculator calculator = new MemorySizeCalculator(context);
int defaultMemoryCacheSize = calculator.getMemoryCacheSize();
int defaultBitmapPoolSize = calculator.getBitmapPoolSize();

如果在應用當中想要調整記憶體快取的大小,開發者可以通過如下方式:
Glide.get(context).setMemoryCategory(MemoryCategory.HIGH);

2.2 Memory Cache

Glide記憶體快取的目的是減少I/O,提高效率

可以通過GlideBuidler的setMemoryCache(MemoryCache memoryCache)去設定快取的大小,開發者可以通過LruResourceCache類去設定快取區的大小

builder.setMemoryCache(new LruResourceCache(yourSizeInBytes));

2.3 Bitmap Pool

可以通過GlideBuilder的setBitmapPool()方法設定池子的大小,LruBitmapPool是Glide的預設實現,使用如下:

builder.setBitmapPool(new LruBitmapPool(sizeInBytes));
2.4 圖片格式

GlideBuilder允許開發者設定一個全域性的預設圖片格式,

在預設情況下,Glide使用RGB_565格式載入圖片,如果想要使用高質量的圖片,可以通過如下方式設定系統的圖片格式:

builder.setDecodeFormat(DecodeFormat.ALWAYS_ARGB_8888);

九、自定義顯示控制元件

除了可以將圖片、視訊快照和GIFS顯示在View上面之外,開發者也可以在自定義的Target上面顯示這些媒體檔案

1.SimpleTarget

重點內容

如果你想簡單地載入一個Bitmap,你可以通過以下簡單的方式而不是直接地顯示給使用者,可能是顯示一個notification,或者上傳一個頭像,Glide都能很好地實現

SimpleTarget提供了對Target的簡單實現,並且讓你專注於對載入結果的處理

為了使用SimpleTarget,開發者需要提供一個寬和高的畫素值,用來載入你的資原始檔,並且你需要去實現


onResourceReady(R resource,GlideAnimation<? super R> glideAnimation)
int myWidth = 512;
int myHeight = 384;
 
Glide.with(yourApplicationContext))
  .load(youUrl)
  .asBitmap()
  .into(new SimpleTarget<Bitmap>(myWidth, myHeight) {
    @Override
    public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
      // Do something with bitmap here.
    }
  };

說明:

通常你去載入資源的時候,是將他們載入到一個view當中,當fragment或者activity失去焦點或者distroyed的時候,Glide會自動停止載入相關資源,確保資源不會被浪費

在大多數SimpleTarget的實現當中,如果需要資源的載入不受元件生命週期的影響,Glide.width(context)當中的context是application context而不是fragment或者activity

另外,由於一些long running operations可能會導致記憶體洩露,如果你打算使用一個這樣的操作,可以考慮使用一個靜態的內部類而不是一個動態的內部類。

2.ViewTarget

如果你想載入一張圖片到一個view當中,但是又想改變或者監聽Glide預設的部分設定,就可以通過重寫ViewTarget或者他的子類來實現

如果你想Gidle載入圖片的時候可以自定義圖片的大小,或者想要設定一個自定義的顯示動畫,就可以通過ViewTarget來實現,可以通過一個靜態的ViewTarget或者動態的內部類來實現相關的功能

Glide.with(yourFragment)
 .load(yourUrl)
 .into(new ViewTarget<YourViewClass, GlideDrawable>(yourViewObject) {
   @Override
   public void onResourceReady(GlideDrawable resource, GlideAnimation anim) {
     YourViewClass myView = this.view;
     // Set your resource on myView and/or start your animation here.
   }
 });

說明:

載入一張靜態的圖片或者一張GIF動態圖,可以在load後面加上asBitmap()/asGif()

.Load(url)會通過asXXX()替換ViewTarget當中的GlideDrawable引數,也可以通過實現LifecycleLisener,給target設定一個回撥。

3.覆蓋預設的相關設定

如果只是想使用Glide的預設配置,可以使用Glide當中ImageViewTargets的兩個子類:

GlideDrawableImageViewTarget 預設的實現,可以通過asGif()載入動態圖片

BitmapImageViewTarget 可以通過asBitmap()載入靜態圖片

如果想要使用Glide預設實現,可以在他們的子類方法當中使用super.xx()即可,例如:


Glide.with(yourFragment)
 .load(yourUrl)
 .asBitmap()
 .into(new BitmapImageViewTarget(yourImageView)) {
   @Override
   public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
     super.onResourceReady(bitmap, anim);
     Palette.generateAsync(bitmap, new Palette.PaletteAsyncListener() { 
       @Override
       public void onGenerated(Palette palette) {
         // Here's your generated palette
       }
     });
   }
 });

十、使用Glide下載自定義尺寸的圖片

Glide的ModelLoader介面為開發者提供了裝載圖片的view的尺寸,並且允許開發者使用這些尺寸資訊去選擇合適的URL去下載圖片。選用適當的尺寸可以節省寬頻和裝置的空間開銷,提高app的效能

2014年googleI/o大會發表了一篇文章,闡述了他們如何使用ModelLoader介面去適配圖片的尺寸,見下面的連線:https://github.com/google/iosched/blob/master/doc/IMAGES.md

1、通過http/https下載圖片,可以通過繼承BaseGlideUtlLoader來實現:

public interface MyDataModel {
  public String buildUrl(int width, int height);
} 
public class MyUrlLoader extends BaseGlideUrlLoader<MyDataModel> {
  @Override
  protected String getUrl(MyDataModel model, int width, int height) {
    // Construct the url for the correct size here.
    return model.buildUrl(width, height);
  }
}
2、可以使用你自定義的ModelLoader去載入圖片了
Glide.with(yourFragment)
 .using(new MyUrlLoader())
 .load(yourModel)
 .into(yourView);

如果你想避免每次載入圖片都要使用.using(new MyUrlLoader()) ,可以實現是一個ModelLoaderFactory然後使用Glide將它註冊到GlideModule當中
public class MyGlideModule implements GlideModule {
  ...
  @Override
  public void registerComponents(Context context, Glide glide) {
    glide.register(MyDataModel.class, InputStream.class, 
      new MyUrlLoader.Factory());
  }
}

這樣你就可以跳過.using()了
Glide.with(yourFragment)
 .load(yourModel)
 .into(yourView);


十一、在後臺執行緒當中進行載入和快取

為了保證Glide在後臺執行緒當中載入資原始檔更加容易,Glide除了Glide.with(fragment).load(url).into(view)之外還提供了

downloadOnly(int width, int height)
downloadOnly(Y target)// Y extends Target<File>
into(int width, int height)


1.downloadOnly

Glide的downloadOnly()允許開發者將Image的二進位制檔案下載到硬碟快取當中,以便在後續使用,

在UI執行緒當中非同步下載,在非同步執行緒當中則是使用width和height

在非同步執行緒當中同步呼叫下載,在同步執行緒當中,

downloadOnly使用一個target作為引數

(1)在後臺執行緒當中下載圖片,可以通過如下的方式:

FutureTarget<File> future = Glide.with(applicationContext)
  .load(yourUrl)
  .downloadOnly(500, 500);
File cacheFile = future.get();

當future返回的時候,image的二進位制檔案資訊就存入了disk快取了,值得注意的是downloadOnly API只是保證圖片個bytes資料在disk當中是有效的。

(2)下載完畢之後如果想要進行顯示,可以通過如下方式進行呼叫:

Glide.with(yourFragment)
 .load(yourUrl)
 .diskCacheStrategy(DiskCacheStrategy.ALL)
 .into(yourView);


通過DiskCacheStrategy.ALL或者DiskCacheStrategy.SOURCE,可以保證程式會去讀取快取檔案

2. 如果想要在後臺執行緒當中獲取某個URL對應的Bitmap

不通過downloadOnly,可以使用into(),會返回一個FutureTarget物件,比如,想要得到一個URL對應的500*500的centerCrop裁剪圖片,可以通過如下方式實現:

Bitmap myBitmap = Glide.with(applicationContext)
 .load(yourUrl)
 .asBitmap()
 .centerCrop()
 .into(500, 500)
 .get()

注意:上面的呼叫只能在非同步執行緒當中,如果在main Thread當中呼叫.get(),會阻塞主線

十二、轉換器

1.預設的轉換器

Glide兩個預設的轉換器,fitCenter和CenterCrop,其他的轉換器詳見https://github.com/wasabeef/glide-transformations,可以將圖片轉為各種形狀,例如圓形,圓角型等等

用法
Glide.with(yourFragment)
  .load(yourUrl)
  .fitCenter()
  .into(yourView);
 
 
 Glide.with(yourFragment)
  .load(yourUrl)
  .centerCrop()
  .into(yourView);
 
 
 // For Bitmaps:
Glide.with(yourFragment)
  .load(yourUrl)
  .asBitmap()
  .centerCrop()
  .into(yourView);
// For gifs:
Glide.with(yourFragment)
  .load(yourUrl)
  .asGif()
  .fitCenter()
  .into(yourView);

甚至可以在兩幅圖片進行型別轉換的時候進行transformed
Glide.with(yourFragment)
 .load(yourUrl)
 .asBitmap()
 .toBytes()
 .centerCrop()
 .into(new SimpleTarget<byte[]>(...) { ... });

2.自定義轉換器

方法:

第一步、編寫轉換器類 ,繼承BitmapTransformation:

private static class MyTransformation extends BitmapTransformation {
  public MyTransformation(Context context) {
    super(context);
  }
  @Override
  protected Bitmap transform(BitmapPool pool, Bitmap toTransform, 
      int outWidth, int outHeight) {
    Bitmap myTransformedBitmap = ... // apply some transformation here.
    return myTransformedBitmap;
  }
  @Override
  public String getId() {
    // Return some id that uniquely identifies your transformation.
    return "com.example.myapp.MyTransformation";
  }
}
第二步、在Glide方法鏈當中用.transform(…)替換fitCenter()/centerCrop()
Glide.with(yourFragment)
  .load(yourUrl)
  .transform(new MyTransformation(context))
  .into(yourView);
// For Bitmaps:
Glide.with(yourFragment)
  .load(yourUrl)
  .asBitmap()
  .transform(new MyTransformation(context))
  .into(yourView);
// For Gifs:
Glide.with(yourFragment)
  .load(yourUrl)
  .asGif()
  .transform(new MyTransformation(context))
  .into(yourView);

3.自定義轉換器的尺寸

在上面使用過程當中沒有設定尺寸值,那麼轉換器轉換的圖片尺寸怎麼確定呢,

Glide實際上已經足夠智慧根據view的尺寸來確定轉換圖片的尺寸了

如果需要自定義尺寸,而不是用view和target當中的尺寸,那麼可以使用override(int,int)設定相關的寬和高

4. Bitmap 再利用

為了減少垃圾收集,可以通過BitmapPool介面去釋放不需要的Bitmaps,當然也可以對裡面的bitmap進行再利用。

例如在一次轉換中,

從pool當中得到一個bitmap

將Bitmap回設給Canvas

使用Matrix、Paint在Canvas上面繪製原始的Bitmap,或者通過一個Shader來轉換一個image

4.1 不要手動地去釋放一個轉換的bitmap資源,也不要將transform()之後的Bitmap重新放置到BitmapPool當中去

protected Bitmap transform(BitmapPool bitmapPool, Bitmap original, int width, int height) {
  Bitmap result = bitmapPool.get(width, height, Bitmap.Config.ARGB_8888);
  // If no matching Bitmap is in the pool, get will return null, so we should //allocate.
  if (result == null) {
    // Use ARGB_8888 since we're going to add alpha to the image.
    result = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
  }
  // Create a Canvas backed by the result Bitmap.
  Canvas canvas = new Canvas(result);
  Paint paint = new Paint();
  paint.setAlpha(128);
  // Draw the original Bitmap onto the result Bitmap with a transformation.
  canvas.drawBitmap(original, 0, 0, paint);
  // Since we've replaced our original Bitmap, we return our new Bitmap here. Glide will
  // will take care of returning our original Bitmap to the BitmapPool for us. 
  return result;
}




常見問題

為什麼有的圖片第一次載入的時候只顯示佔位圖,第二次才顯示正常的圖片呢?

為什麼 我總會得到類似You cannot start a load for a destroyed activity這樣的異常呢?

為什麼我不能給載入的圖片setTag()呢?

為什麼?為什麼?這麼NB的庫竟然會有這麼多的問題。沒錯,這就是我今天要講的重點。怎麼避免上面的問題發生。

一些解決方案

1.如果你剛好使用了這個圓形Imageview庫或者其他的一些自定義的圓形Imageview,而你又剛好設定了佔位的話,那麼,你就會遇到第一個問題。如何解決呢?

方案一: 不設定佔位;

方案二:使用GlideTransformation API自定義圓形Bitmap的轉換。這裡是一個已有的例子;

方案三:使用下面的程式碼載入圖片:

Glide.with(mContext)
    .load(url) 
    .placeholder(R.drawable.loading_spinner)
    .into(new SimpleTarget<Bitmap>(width, height) {
        @Override 
        public void onResourceReady(Bitmap bitmap, GlideAnimation anim) {
            // setImageBitmap(bitmap) on CircleImageView 
        } 
    };

2.至於第二個問題,請記住一句話:不要再非主執行緒裡面使用Glide載入圖片,如果真的使用了,請把context引數換成getApplicationContext。更多的細節請參考這個issue(https://github.com/bumptech/glide/issues/138)

The key is ViewTarget.setTagId; setting it will free up the default setTag on the ImageView so you can use it as root in the item layout. It was introduced in Glide 3.6.0 in issue #370.

In your manifest add this:

<application
        android:name=".App"

Then create an application context class:

public class App extends Application {
    @Override public void onCreate() {
        super.onCreate();
        ViewTarget.setTagId(R.id.glide_tag);
    }
}

Add the following as contents for src/main/values/ids.xml:

<resources>
    <item type="id" name="glide_tag" />
</resources>

(or just add the above <item ... /> into any resources xml in values)


3.為什麼不能設定Tag,是因為你使用的姿勢不對哦。如何為ImageView設定Tag呢?且聽我細細道來。

方案一:使用setTag(int,object)方法設定tag,具體用法如下:

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(imageViewHolder.image);
        imageViewHolder.image.setTag(R.id.image_tag, i);
        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
                int position = (int) v.getTag(R.id.image_tag);
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });

同時在values資料夾下新建ids.xml,新增

<item name="image_tag" type="id"/>

大功告成!

方案二:從Glide3.6.0之後,新添加了全域性設定的方法。具體方法如下:

先實現GlideMoudle介面,全域性設定ViewTagettagId:

public class MyGlideMoudle implements GlideModule{
    @Override
    public void applyOptions(Context context, GlideBuilder builder) {
        ViewTarget.setTagId(R.id.glide_tag_id);
    }

    @Override
    public void registerComponents(Context context, Glide glide) {

    }
}


同樣,也需要在ids.xml下新增id

<item name="glide_tag_id" type="id"/>

最後在AndroidManifest.xml檔案裡面新增

<meta-data

    android:name="com.yourpackagename.MyGlideMoudle"

    android:value="GlideModule" />

又可以愉快的玩耍了,嘻嘻`(_)′

方案三:寫一個繼承自ImageViewTaget的類,複寫它的get/setRequest方法。

Glide.with(context).load(urls.get(i).getUrl()).fitCenter().into(new ImageViewTarget<GlideDrawable>(imageViewHolder.image) {
            @Override
            protected void setResource(GlideDrawable resource) {
                imageViewHolder.image.setImageDrawable(resource);
            }

            @Override
            public void setRequest(Request request) {
                imageViewHolder.image.setTag(i);
                imageViewHolder.image.setTag(R.id.glide_tag_id,request);
            }

            @Override
            public Request getRequest() {
                return (Request) imageViewHolder.image.getTag(R.id.glide_tag_id);
            }
        });

        imageViewHolder.image.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = (int) v.getTag();
                Toast.makeText(context, urls.get(position).getWho(), Toast.LENGTH_SHORT).show();
            }
        });


補充說明:

1.placeholder() 佔位圖或者失敗圖都可以直接使用R.color.white 顏色來做;

2.如果載入不出來圖片的話可以試試設定下圖片的寬高;

3.圖片的載入並不是全快取,而是使用的imageview的大小來的,如果想要全快取的就可以這樣:

.diskCacheStrategy(DiskCacheStrategy.ALL)

4.圖片載入背景會變成綠色,載入jpg圖片會出現的BUG,github上給出解決方案

Glide.with(this).load(url).diskCacheStrategy(DiskCacheStrategy.SOURCE).into(imageView); 或者

Glide.with(this).fromResource().asBitmap().encoder(newBitmapEncoder(Bitmap.CompressFormat.PNG,100)).load(R.drawable.testimg).into(imageView);

5.圓形圖片用V4包自帶的處理方式:

Glide.with(context).load(imageUrl).asBitmap().fitCenter().diskCacheStrategy(DiskCacheStrategy.SOURCE)

.placeholder(R.drawable.shape_glide_round_place).error(R.drawable.no_face_circle)

.into(newBitmapImageViewTarget(imageView) {

@Override

protected voidsetResource(Bitmap resource) {

RoundedBitmapDrawable circularBitmapDrawable =

RoundedBitmapDrawableFactory.create(context.getResources(),resource);

circularBitmapDrawable.setCircular(true);

imageView.setImageDrawable(circularBitmapDrawable);

}

});

5.預設的動畫可以取消掉,加上dontAnimate()。

6.清除快取:

Glide.get(this).clearMemory();               
Glide.get(this).clearDiskCache(); 需要在子執行緒執行

7. 載入暫時不支援顯示進度,可以用佔