Glide V4使用指南

Glide的強大和靈活相信不需要多介紹了
本文使用Glide版本為 4.8.0 ,因為使用的Java語言進行開發,涉及到使用Kotlin的部分還請參考 ofollow,noindex">官方文件
SDK要求
- 最小SDK版本需要使用
API 14
(或者更高版本) -
Complie SDK Version
需要使用API 27
(或者更高版本) -
Glide
使用的SupportLibrary
版本是27,如果需要不同的SupportLibrary
版本可以用exclude
將Glide
的SupportLibrary
從依賴中去掉,具體的在整合時說明
一、整合
在專案的 build.gradle
檔案中新增 google()
倉庫
repositories { google() //or maven { url 'https://maven.google.com' } jcenter() }
在要使用的 Glide
的 module
中新增以下程式碼
implementation('com.github.bumptech.glide:glide:4.8.0') annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0' //如果使用了在Kotlin中使用了Glide註解,需要引入kapt依賴代替annotationProcessor依賴 //kapt 'com.github.bumptech.glide:compiler:4.8.0'
若使用了不是27的 SupportLibrary
版本,使用以下程式碼
implementation('com.github.bumptech.glide:glide:4.8.0') { exclude group: "com.android.support" } annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
使用了不同版本的 SupportLibrary
可能導致一些執行時異常,
java.lang.NoSuchMethodError: No static method getFont(Landroid/content/Context;ILandroid/util/TypedValue;ILandroid/widget/TextView;)Landroid/graphics/Typeface; in class Landroid/support/v4/content/res/ResourcesCompat; or its super classes (declaration of 'android.support.v4.content.res.ResourcesCompat' at android.support.v7.widget.TintTypedArray.getFont(TintTypedArray.java:119)
儘量避免使用 @aar
,如果需要這麼做,需要新增 transitive=true
確保有所需要的類。
dependencies { implementation ("com.github.bumptech.glide:glide:4.8.0@aar") { transitive = true } }
@aar
是 Gradle
的限制符,預設會去除使用到得依賴。如果不新增 transitive=true
的話將會導致執行時異常。
java.lang.NoClassDefFoundError: com.bumptech.glide.load.resource.gif.GifBitmapProvider at com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder.<init>(ByteBufferGifDecoder.java:68) at com.bumptech.glide.load.resource.gif.ByteBufferGifDecoder.<init>(ByteBufferGifDecoder.java:54) at com.bumptech.glide.Glide.<init>(Glide.java:327) at com.bumptech.glide.GlideBuilder.build(GlideBuilder.java:445) at com.bumptech.glide.Glide.initializeGlide(Glide.java:257) at com.bumptech.glide.Glide.initializeGlide(Glide.java:212) at com.bumptech.glide.Glide.checkAndInitializeGlide(Glide.java:176) at com.bumptech.glide.Glide.get(Glide.java:160) at com.bumptech.glide.Glide.getRetriever(Glide.java:612) at com.bumptech.glide.Glide.with(Glide.java:684)
許可權
這個不需要多說,如果載入網路圖片必然需要網路許可權,這個還是根據具體的使用情況而定。
<uses-permission android:name="android.permission.INTERNET" />
Glide
添加了連結監聽(Connectivity Monitoring),通過網路載入圖片時,Glide可以監聽連結狀態並在重新連結到網路時重啟之前失敗的請求。使用連結監聽我們需要新增 ACCESS_NETWORK_STATE
許可權,Glide將自動監聽連線狀態,不需要額外的改動。
如果需要使用 ExternalPreferredCacheDiskCacheFactory
將Glide的快取存放到SD卡上,還需要新增 READ_EXTERNAL_STORAGE
和 WRITE_EXTERNAL_STORAGE
許可權。
混淆
如果使用了混淆,需要新增以下的程式碼到混淆的配置檔案中
-keep public class * implements com.bumptech.glide.module.GlideModule -keep public class * extends com.bumptech.glide.module.AppGlideModule -keep public enum com.bumptech.glide.load.ImageHeaderParser$** { **[] $VALUES; public *; }
如果使用的target API低於27的話還需要新增
-dontwarn com.bumptech.glide.load.resource.bitmap.VideoDecoder
VideoDecoder
使用 API 27
的一些藉口,可能導致混淆發出警告。
如果使用了 DexGuard
,可能還需要新增
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
二、簡單使用
V4的基本用法還是沒變,一句簡單地鏈式呼叫就能講圖片載入到 ImageView
上了
Glide.with(context) .load(url) .into(imageView);
這裡 context
就是一個 Context
物件,而 url
是一個網路圖片連結, imageView
則是需要顯示圖片的 ImageView
。V4還可以手動取消載入,例如:
Glide.with(context).clear(imageView);
Glide.with()
中傳入的 Activity
或 Fragment
銷燬時, Glide
會自動取消載入並回收資源
這裡會提出 clear
方法,在 RecyclerView
中複用 View
來載入圖片,每次需要呼叫 Glide
重新進行載入操作或者使用 clear
方法停止之前的請求,避免圖片顯示錯亂的問題。
可以看到 with
方法還是很強大,支援 Context
, Activity
, Fragment
, View
等,只是將原本支援傳入 android.app.Fragment
的方法標記為過期了。
load
方法也提供了強大的支援,Uri、檔案、btye[]、網路連線、資源id、bitmap、drawable都可以載入。
Generated API(生成API)
Glide V4使用註解處理器生成一個流式API,用於 RequestBuilder
, RequestOptions
等相關的所有選項。Glide的文件中有說明,其目的一是為了更好地擴充套件自定義選項,其二是為了方便打包常用選項組。
對 RequestOptions
的設定,Glide V4還提供了 apply
方法設定單次請求的選項,以及 applyDefaultRequestOptions
設定預設請求選項。
從Glide V3到Glide V4的升級,生成API是一個很大的改變,接下來詳細說明。
生成API目前只能在 Application
模組中使用,無法同時在各個 Library
和 Application
中定義各自的生成API,確保了呼叫API時請求的選項是一致的。官方也指出,在將來的版本中可能解除此限制。
使用Generated API僅僅需要兩步,第一步,在 build.gradle
的依賴中新增 annotationProcessor 'com.github.bumptech.glide:compiler:4.8.0'
上面其實已經給出;第二步,我們需要建立一個 class
繼承 AppGlideModule
,併為該類新增 @GlideModule
註解。
package com.mrtrying.glidev4_example; import com.bumptech.glide.annotation.GlideModule; import com.bumptech.glide.module.AppGlideModule; GlideModule public final class ExampleAppGlideModule extends AppGlideModule {}
AppGlideModule
雖然是抽象類,卻可以不用重寫任何方法。但必須使用 @GlideModule
註解標記該類,否則沒法順利的生成 GlideApp
的 API
。
PS:第一次新增 AppGlideModule
或者對 AppGlideModule
做了某些修改時,我們需要重新構建專案重新生成API。如果AndroidStudio沒法自動完成構建,可以使用 Build—->Rebuild Project
手動重新構建,必要時可能需要手動刪除 module
的 build
目錄再重新構建專案。
生成API的預設名為 GlideApp
,其包名與所在的 module
的包名相同,而基本用法也於之前相同,只是將 Glide
替換成了 GlideApp
,就可以使用了
GlideApp.with(this).load(url).into(imageView);
Glide生成API還有 GlideExtension
、 GlideOption
、 GlideType
,暫時只對 AppGlideModule
進行說明,其他的涉及到較深度的使用,放在後面說明。
佔位符
Glide提供了三種不同型別的佔位符:
- placeholer(佔位符)
- error(錯誤符)
- fallback(後備回撥符)
GlideApp.with(this) .load(url) .placeholder(R.mipmap.placeholder) .error(R.mipmap.error) .fallback(R.mipmap.fallback) .into(imageView);
先說API的引數, placeholder
、 error
、 fallback
相同可以傳入資源id或者Drawable物件。
接下來說說這三個佔位符的呼叫時機,用過V3的同學一定知道 placeholer
佔位符是當資源正在請求是被展示的圖片或者資原始檔,當請求成功時佔位符會被替換成請求到得資源; error
則是在請求永久性失敗是展示; fallback
是當請求的url(或者model)為 null
時展示,此佔位符的目的是設定 null
是否是可接收的正常情況,而Glide將 null
作為錯誤處理。
三個佔位符對應了三種時機,其中還有一些狀態,無可避免的會出現一些複雜的情況需要說明:
- 設定了
placeholder
之後,如果請求失敗,但是沒有設定error
,那麼佔位符將繼續顯示;而請求的url/model為null也會導致請求失敗,一樣的,沒有設定error
和fallback
的話,佔位符也將繼續顯示 - 如果請求的url/model為null導致請求失敗,
fallback
會優先於error
呼叫,也就是說url為null時設定了error
而沒有設定fallback
將繼續顯示error
;反之,設定了fallback
就會顯示fallback
佔位符這個功能確實很棒,有朋友不經要問了,如果我想載入網路圖片怎麼辦?這個就十分抱歉了,Glide並不提供這樣的功能,只能支援資原始檔或者是 Drawable
物件。當然,你也可以提前下載要網路圖片在以 Drawable
形式載入,只要這樣的效率你可以接收(個人覺得沒有什麼必要)。
官方文件在佔位符最後的FAQ中有說明,佔位符是在主執行緒中,從Android Resource載入的,而且Glide的 Transformation
不會被應用到佔位符上。這都是希望在使用佔位符時能儘量少得佔用系統資源來考慮的,所以Glide並沒有提供他們認為不太合理的API。
關於圖片的變換,比較常用到得是圓角和模糊,圓角的處理的話比較推薦 RCLayout ,雖然在背景的處理上會有一點鋸齒,不過還是很不錯的一個庫;而模糊的話推薦使用android自帶的 RenderScript
,相關方法在 API 17
以上提供,也提供了相關的 support
包。
transformation(變換)
Glide提供了 transformation
得功能,在獲取到請求的圖片之後,能對圖片進行一些處理,例如:裁剪、模糊等;而 transformation
的強大在於可以自定義,這樣一來 transformation
不僅能處理 bitmap
,同樣可以用於處理GIF動畫,還有自定義資源型別。
Glide在API上提供的了5個相關的方法可以直接使用
- circleCrop()
- centerCrop()
- centerInside()
- fitCenter()
- optionalFitCenter()
optionalFitCenter()
的具體作用沒有太清楚,效果與 fitCenter()
相同,有知道的大佬請告知
使用方式基本和佔位符一致,在 load()
方法後呼叫相關方法就能使用相應的 transformation
效果了
GlideApp.with(this) .load(url) .centerCrop() .into(imageView);
這是在生成API中的使用,在Glide普通的API也是可以使用的,不過沒有直接可以呼叫的方法,需要通過 RequestOptions
來設定
RequestOptions options = new RequestOptions().centerCrop(); Glide.with(this) .load(url) .apply(options) .into(imageView);
除了可以自己建立以外,Glide提供了靜態獲取 RequestOptions
方法,可以直接使用
Glide.with(this) .load(url) .apply(RequestOptions.centerCropTransform()) .into(imageView);
針對 ImageView
可能自身也會設定 scaleType
的情況,Glide在部分情況會自動應用 FitCenter
或 CenterCrop
,如果 scaleType
是 CENTER_CROP
, Glide
將會自動應用 CenterCrop
變換。如果 scaleType
為 FIT_CENTER
或 CENTER_INSIDE
, Glide
會自動使用 FitCenter
變換。
當然,設定了其他的變換也可以將其覆蓋;或者使用 dontTransform()
方法就不會在執行任何變換。
這裡需要注意的是,Glide內建的這幾種 transformation
,即使在使用多種變換,也只有最後一個 transformation
會生效。這個不僅僅是對Glide內建的 transformation
也包括 transform()
設定的 transformation
,都會替換掉之前的 transformation
。
如果需要支援多種變換,需要使用 transform()
設定 MultiTransformation
類物件傳入, MultiTransformation
的構造器可以接收可變引數或者 Transformation
的集合;或者在 transforms()
方法中設定多個(不要以為是一個方法,這是 transforms()
,結尾有 s
的)而這些個變化的應用順序就是傳入引數的順序。
GlideApp.with(this) .load(url) .transform(new MultiTransformation<>(new FitCenter(),new RoundedCorners(3))) .into(imageView); //或者 GlideApp.with(this) .load(url) .transforms(new FitCenter(),new RoundedCorners(3)) .into(imageView);
由於 Transformation
是沒有狀態的,我們可以再多個載入中複用同一個 Transformation
物件。
自定義transformation
為了方便使用,我們希望一些特殊的變換也能像上面一樣的使用,這種情況可以通過實現 Transformation
來處理。
這裡直接以Glide內建實現的 RoundedCorners
為例
public final class RoundedCorners extends BitmapTransformation { private static final String ID = "com.bumptech.glide.load.resource.bitmap.RoundedCorners"; private static final byte[] ID_BYTES = ID.getBytes(CHARSET); private final int roundingRadius; /** * @param roundingRadius the corner radius (in device-specific pixels). * @throws IllegalArgumentException if rounding radius is 0 or less. */ public RoundedCorners(int roundingRadius) { Preconditions.checkArgument(roundingRadius > 0, "roundingRadius must be greater than 0."); this.roundingRadius = roundingRadius; } @Override protected Bitmap transform( @NonNull BitmapPool pool, @NonNull Bitmap toTransform, int outWidth, int outHeight) { return TransformationUtils.roundedCorners(pool, toTransform, roundingRadius); } @Override public boolean equals(Object o) { if (o instanceof RoundedCorners) { RoundedCorners other = (RoundedCorners) o; return roundingRadius == other.roundingRadius; } return false; } @Override public int hashCode() { return Util.hashCode(ID.hashCode(), Util.hashCode(roundingRadius)); } @Override public void updateDiskCacheKey(@NonNull MessageDigest messageDigest) { messageDigest.update(ID_BYTES); byte[] radiusData = ByteBuffer.allocate(4).putInt(roundingRadius).array(); messageDigest.update(radiusData); } }
可以看到 RoundedCorners
是繼承自 BitmapTransformation
, BitmapTransformation
已經幫我們處理了部分基礎邏輯,比如提取和回收原始的 Bitmap
,而我們處理好 Bitmap
的變換就可以了。
除了自身的建構函式意外還重寫了4個方法,其中 transform
方法就是進行變換處理的方法,變換完成之後返回變換完成的 Bitmap
。其餘的三個方法, updateDiskCacheKey
是必須實現的,而為例保證記憶體和磁碟快取正常, equals()
和 hashCode()
也是必須重寫的。
RoundedCorners
使用完整報名路徑的限定名來作為一個 ID,它可以構成 hashCode()
的基礎,並可用於更新 updateDiskCacheKey()
傳入的 MessageDigest
。如果你的 Transformation 需要引數而且它會影響到 Bitmap 被變換的方式,它們也必須被包含到這三個方法中,就像 RoundedCorners
一樣。原來的ID保留,但 roundingRadius
也包含到了這個三個方法中, updateDiskCacheKey
方法還演示了你可以如何使用 ByteBuffer
來包含基本引數到你的 updateDiskCacheKey
實現中。
官方文件著重指出必須要重寫 equals()
和 hashCode()
方法,雖然不重寫不會出現編譯問題,但是這不代表能正常工作。
三、進階
Target
或許在使用 into()
時有些印象, into()
方法可以直接傳入 ImageView
也可以使用 Target
。其實跟蹤一下Glide的原始碼就會發現其實在傳入 ImageView
時,最終也在 ImageViewTargetFactory
的包裝下返回了 ImageViewTarget
,而 ViewTarget
的最上層的父類就是 Target
。
into()
方法不僅用於啟動每一個請求,同時也制定了接收請求結果的 Target
。
Target<Drawable> target = GlideApp.with(this) .load(url) .centerCrop() .into(new Target<Drawable>() { //Target的方法太多,這裡程式碼省略... });
如果重用同一個 Target
對載入一個新的請求,那麼之前的的請求都會被取消並且釋放資源。我們也可以使用 clear()
方法對不需要重新載入的請求進行相關資源的釋放。
GlideApp.with(this).clear(target);
上面的情況中直接使用 ImageVIew
也是可以的。因為 ViewTarget
使用了 setTag()
和 getTag()
儲存了 Request
,所以可以直接從 View
的 tag
取回之前一次載入的資訊,也是是 ImageVIew
的預設 tag
被佔用的原因。
也就是說,在使用ImageVIew的情況中,即使使用同一個ImageView重新載入也是可以釋放之前的請求和資源的
Glide.with(this) .load(url) .into(imageView); //載入新連線 Glide.with(this) .load(newUrl) .into(imageView);
只要繼承 ViewTarget
或者重寫 setRequest()
和 getRequest()
並實現取回上一次載入的資訊,重用的機制就可以得以保證。
Transitions(過渡)
Glide V3和V4不同,不會預設應用交叉淡入或任何其他的過渡效果,每個請求需要手動應用過渡。Glide提供了很多過渡動畫,我們可以手動應用於每一個請求上;內建過渡的執行方式是一致的,會根據載入影象不同的情況來決定是否執行過渡。例如:如果Glide從記憶體快取中加載出來,Glide的內建過渡將不會執行;而載入磁碟快取、本地檔案或者遠端連線時都會執行Glide的內建過渡。
可以通過 transition()
方法設定 TransitionOptions
Glide.with(this) .load(url) .transition(DrawableTransitionOptions.withCrossFade()) .into(imageView);
TransitionOptions
用於給一個特定的請求指定過渡,而不同的資源型別能決定使用什麼型別的過渡選項。Bitmap 和 Drawable可以對應使用使用 BitmapTransitionOptions
或 DrawableTransitionOptions
來指定型別特定的過渡動畫。對於 Bitmap
和 Drawable
之外的資源型別,可以使用 GenericTransitionOptions
。
如果需要自定義過渡動畫,我們需要通過 DrawableTransitionOptions.with()
生成我們自己的 TransitionOptions
,而 with()
需要出入一個 TransitionFactory
物件。 TransitionFactory
是一個介面我們需要實現一個這樣的類
public class ExampleTransitionFactory implements TransitionFactory { @Override public Transition build(DataSource dataSource, boolean isFirstResource) { return null; } }
通過 DrawableTransitionOptions.with(new ExampleTransitionFactory())
我們就能呼叫 transition()
方法來載入我們的定製的過渡動畫。 transition()
方法支援 動畫的資源 id, Animator
和 TransitionFactory
,可以通過這三種方式來實現動畫部分。

最後就是執行 transition()
來載入我們自定義的動畫
GlideApp.with(this) .load(url) .transition(DrawableTransitionOptions.with(new ExampleTransitionFactory()).transition(R.anim.show)) .into(imageView);
這裡需要提醒的是,動畫對於效能的開銷不用多說,比圖片解碼本身還要耗時,在列表的快速滑動的情況下可能造成載入緩慢。在列表中考慮是否使用動畫,在一些希望圖片儘快加載出來的時候也需要做此考慮。