Glide 4.0使用
Glide v4 使用 注解處理器 (Annotation Processor) 來(lái)生成出一個(gè) API惊楼,在 Application 模塊中可使用該流式 API 一次性調(diào)用到 RequestBuilder
, RequestOptions
和集成庫(kù)中所有的選項(xiàng)跳夭。
有效使用范圍
Generated API 目前僅可以在 Application 模塊內(nèi)使用蓬网。這一限制可以讓我們僅持有一份 Generated API瘸味,而不是各個(gè) Library 和 Application 中均有自己定義出來(lái)的 Generated API切端。這一做法會(huì)讓 Generated API 的調(diào)用更簡(jiǎn)單,并確保 Application 模塊中 Generated API 調(diào)用的選項(xiàng)在各處行為一致寂祥。
導(dǎo)入
annotationProcessor 'com.github.bumptech.glide:compiler:4.7.1' //注解依賴包
implementation 'com.github.bumptech.glide:glide:4.7.1' //api包
//Generated API 必須要導(dǎo)入上述注解依賴包才可以使用
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
}
Generated API
Generated API 默認(rèn)名為 GlideApp
衣吠,與 Application 模塊中 AppGlideModule
的子類包名相同。在 Application 模塊中將 Glide.with()
替換為 GlideApp.with()
壤靶,即可使用該 API 去完成加載工作
GlideApp.with(fragment)
.load(myUrl)
.placeholder(R.drawable.placeholder)
.fitCenter()
.into(imageView);
與 Glide.with()
不同,諸如 fitCenter()
和 placeholder()
等選項(xiàng)在 Builder 中直接可用惊搏,并不需要再傳入單獨(dú)的 RequestOptions
對(duì)象贮乳。
占位符
加載占位符(placeholder): 加載圖片時(shí)展示,加載失敗時(shí)沒(méi)有設(shè)置error,顯示的還是placeholder
錯(cuò)誤符(error):加載失敗時(shí)展示,如果加載的url為null,且沒(méi)有設(shè)置fallback時(shí)展示error
后備回調(diào)符(fallback): 在請(qǐng)求的url為
null
時(shí)展示,表示允許用戶url為null,比如用戶頭像還未設(shè)置時(shí),展示的默認(rèn)圖片
使用
generated api
GlideApp.with(fragment)
.load(url)
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.fallback(R.drawable.fallback)
.into(view)
Glide方式
RequestOptions options=new RequestOptions();
options
.placeholder(R.drawable.placeholder)
.error(R.drawable.error)
.fallback(R.drawable.fallback);
Glide.with(this).load(url).apply(options).into(view);
補(bǔ)充
占位符是異步加載的嗎?
No恬惯。占位符是在主線程從Android Resources加載的向拆。我們通常希望占位符比較小且容易被系統(tǒng)資源緩存機(jī)制緩存起來(lái)。
變換是否會(huì)被應(yīng)用到占位符上酪耳?
No浓恳。Transformation僅被應(yīng)用于被請(qǐng)求的資源,而不會(huì)對(duì)任何占位符使用碗暗。例如你正在加載圓形圖片颈将,你可能希望在你的應(yīng)用中包含圓形的占位符。但是你也可以考慮自定義一個(gè)View來(lái)剪裁(clip)你的占位符言疗,達(dá)到你的transformation的效果晴圾。
在多個(gè)不同的View上使用相同的Drawable可行么?
通吃胙伲可以死姚,但不是絕對(duì)的。任何無(wú)狀態(tài)(non-stateful
)的 Drawable(例如 BitmapDrawable
)通常都是ok的勤篮。但是有狀態(tài)的 Drawable 不一樣都毒,在同一時(shí)間多個(gè) View 上展示它們通常不是很安全,因?yàn)槎鄠€(gè)View會(huì)立刻修改(mutate
) Drawable 碰缔。對(duì)于有狀態(tài)的 Drawable 账劲,建議傳入一個(gè)資源ID,或者使用 newDrawable()
來(lái)給每個(gè)請(qǐng)求傳入一個(gè)新的拷貝手负。
View的mutate
如果同一個(gè)資源文件被兩個(gè)drawable
所使用,那么這兩個(gè)drawable
的狀態(tài)會(huì)保持一致,其中一個(gè)drawable
改變透明度,另外一個(gè)drawable
也會(huì)跟著改變涤垫。這時(shí)如果想要兩個(gè)drawable
獨(dú)立,就需要用到mutate
drawableA.mutate().setAlpha(255);
drawableB.mutate().setAlpha(70);
RequestOptions
Glide4.0之后的原始api中很多樣式的設(shè)置都需要通過(guò)RequestOptions
,包括
- 占位符(
Placeholders
) - 轉(zhuǎn)換(
Transformations
) - 緩存策略(
Caching Strategies
) - 組件特有的設(shè)置項(xiàng)竟终,例如編碼質(zhì)量蝠猬,或
Bitmap
的解碼配置等。
比如要實(shí)現(xiàn)一個(gè)CenterCrop
轉(zhuǎn)換.
RequestOptions cropOptions = new RequestOptions().centerCrop(context);
...
Glide.with(fragment)
.load(url)
.apply(cropOptions)
.into(imageView);
其中apply()可以調(diào)用多次,也就是說(shuō)你可以配置多個(gè)RequestOptions
參數(shù),如果 RequestOptions
對(duì)象之間存在相互沖突的設(shè)置统捶,那么只有最后一個(gè)被應(yīng)用的 RequestOptions
會(huì)生效榆芦。
另外Glide內(nèi)部還提供了靜態(tài)方法來(lái)實(shí)現(xiàn)CenterCrop
轉(zhuǎn)換
import static com.bumptech.glide.request.RequestOptions.centerCropTransform;
Glide.with(fragment)
.load(url)
.apply(centerCropTransform(context))
.into(imageView);
當(dāng)然如果你使用的是Generated API
的話柄粹,就可以直接使用
GlideApp.with(fragment)
.load(url)
.centerCrop()
.into(imageView);
圖片加載過(guò)渡動(dòng)畫(huà)(TransitionOptions)
使用 TransitionOption
可以應(yīng)用以下變換:
- View淡入
- 與占位符交叉淡入
- 或者什么都不發(fā)生
import static com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions.withCrossFade;
Glide.with(fragment)
.load(url)
.transition(withCrossFade()) //交叉淡入
.into(view);
另外TransitionOptions
是特定資源類型獨(dú)有的,即加在bitmap
和drawable
的時(shí)候需要用到不同的api,如 BitmapTransitionOptions ,而不是 DrawableTransitionOptions匆绣。
請(qǐng)求主干RequestOptions
Glide中請(qǐng)求的發(fā)起者是RequestBuilder
這個(gè)對(duì)象驻右。
多個(gè)請(qǐng)求
RequestBuilder<Drawable> requestBuilder =
Glide.with(fragment)
.asDrawable()
.apply(requestOptions);
for (int i = 0; i < numViews; i++) {
ImageView view = viewGroup.getChildAt(i);
String url = urls.get(i);
requestBuilder.load(url).into(view);
}
縮略圖請(qǐng)求
Glide.with(fragment)
.load(url)
.thumbnail(Glide.with(fragment)
.load(thumbnailUrl))
.into(imageView);
縮略圖應(yīng)改指向一個(gè)低分辨率的圖片,這樣就快速加在并在主體圖片加載之前顯示崎淳。
如果只有一個(gè)遠(yuǎn)程的url地址堪夭,可以強(qiáng)制要求加載一個(gè)低分辯率的圖像,通過(guò)sizeMultiplier
Glide.with(this)
.load("")
.thumbnail(0.25f) // 尺寸是View的百分比
.into(imageView);
在Glide4.3之后還提供了一個(gè)失敗加載
Glide.with(fragment)
.load(primaryUrl)
.error(Glide.with(fragment)
.load(fallbackUrl))
.into(imageView);
這個(gè)error請(qǐng)求之后在正常請(qǐng)求失敗之后才會(huì)啟動(dòng).
多重變換
默認(rèn)情況下,每個(gè) transform()
調(diào)用拣凹,或任何特定轉(zhuǎn)換方法(fitCenter()
, centerCrop()
, bitmapTransform()
)的調(diào)用都會(huì)替換掉之前的變換森爽。
其中 MultiTransformation
Glide.with(fragment)
.load(url)
.transform(new MultiTransformation(new FitCenter(), new YourCustomTransformation())
.into(imageView);
自定義變化
可以選擇繼承自BitmapTransformation
或者DrawableTransitionOptions
,
需要注意的是,對(duì)于任何 Transformation
子類嚣镜,包括 BitmapTransformation
爬迟,你都有三個(gè)方法你 必須 實(shí)現(xiàn)它們,以使得磁盤和內(nèi)存緩存正確地工作:
equals()
hashCode()
updateDiskCacheKey
ImageView的自動(dòng)變換
在Glide中菊匿,當(dāng)你為一個(gè) ImageView 開(kāi)始加載時(shí)付呕,Glide可能會(huì)自動(dòng)應(yīng)用 FitCenter 或 CenterCrop ,這取決于view的 ScaleType
跌捆。如果 scaleType
是 CENTER_CROP
, Glide 將會(huì)自動(dòng)應(yīng)用 CenterCrop
變換徽职。如果 scaleType
為 FIT_CENTER
或 CENTER_INSIDE
,Glide會(huì)自動(dòng)使用 FitCenter
變換疹蛉。
當(dāng)然活箕,你總有權(quán)利覆寫(xiě)默認(rèn)的變換,只需要一個(gè)帶有 Transformation
集合的 RequestOptions 即可可款。另外育韩,你也可以通過(guò)使用 dontTransform()
確保不會(huì)自動(dòng)應(yīng)用任何變換
Gif的加載動(dòng)畫(huà)
Glide可以將 Bitmap
Transformation
應(yīng)用到 BitmapDrawable
, GifDrawable
, 以及 Bitmap
資源上,因此通常你只需要編寫(xiě)和應(yīng)用 Bitmap``Transformation
闺鲸。然而筋讨,如果你添加了額外的資源類型,你可能需要考慮派生 RequestOptions
類摸恍,并且悉罕,在內(nèi)置的這些 Bitmap
Transformations
之外,你還需要為你的自定義資源類型提供一個(gè) Transformation
立镶。
目標(biāo)Target
Glide.with(fragment)
.load(url)
.into(imageView); //into() 開(kāi)始啟動(dòng)請(qǐng)求
通常情況下,我們是這樣使用Glide的,其中into(imageView)
這行會(huì)將我們期望展示圖片的容器ImageView
傳遞進(jìn)去壁袄。這時(shí)大部分人會(huì)想Glide
是將下載好的圖片直接賦值給ImgaeView
,實(shí)際上這中間并不是一步到位的,
ViewTarget<ImageView, Drawable> target = Glide.with(this)
.load("")
.into(imageView);
如上述代碼所示into(imageView)
會(huì)返回一個(gè)ViewTarget
對(duì)象,這個(gè)ViewTarget
能對(duì)加載的圖片進(jìn)行各種操作,另外也能返回最開(kāi)始的imageView
,通過(guò)
ImageView image=target.getView();
既然target
是每個(gè)imageView
的包裝,所以每個(gè)target都是唯一的,如果你使用之前的target
來(lái)加載新的圖片,就會(huì)重置之前的操作,將前一步加載的資源釋放掉媚媒。
ViewTarget<ImageView, Drawable> target = Glide.with(this)
.load(url)
.into(imageView);
...
// Some time in the future:
Glide.with(fragment)
.load(newUrl)
.into(target); //這時(shí)老的imageView上的圖片資源會(huì)被釋放,新圖片會(huì)替代來(lái)圖片展示
方法二
Glide.with(this).clear(target);
Glide 的 ViewTarget
子類使用了 Android Framework 的 getTag()
和 setTag()
方法來(lái)存儲(chǔ)每個(gè)請(qǐng)求的相關(guān)信息,因此在使用Glide時(shí),你還需要使用到setTag()
時(shí)需要注意是否會(huì)出現(xiàn)沖突嗜逻。
最簡(jiǎn)單的解決方式是調(diào)用 setTag(int,object)
加載目標(biāo)尺寸
默認(rèn)情況下,Glide 使用目標(biāo)通過(guò) getSize
方法提供的尺寸來(lái)作為請(qǐng)求的目標(biāo)尺寸缭召。這允許 Glide 選取合適的 URL栈顷,下采樣逆日,裁剪和變換合適的圖片以減少內(nèi)存占用,并確保加載盡可能快地完成萄凤。
target.getSize(new SizeReadyCallback(width,height));
默認(rèn)情況下Glide的尺寸加載邏輯
- 如果
View
的布局參數(shù)尺寸 > 0 且 > padding室抽,則使用該布局參數(shù); - 如果
View
尺寸 > 0 且 > padding靡努,使用該實(shí)際尺寸坪圾; - 如果
View
布局參數(shù)為wrap_content
且至少已發(fā)生一次 layout ,則打印一行警告日志惑朦,建議使用Target.SIZE_ORIGINAL
或通過(guò)override()
指定其他固定尺寸神年,并使用屏幕尺寸為該請(qǐng)求尺寸; - 其他情況下(布局參數(shù)為
match_parent
行嗤,0
, 或wrap_content
且沒(méi)有發(fā)生過(guò) layout )垛耳,則等待布局完成栅屏,然后回溯到步驟1。
Transition(過(guò)渡動(dòng)畫(huà))
Transition
指的是從占位符到新加載的圖片堂鲜,或從縮略圖到全尺寸圖像過(guò)渡,而不是從一個(gè)請(qǐng)求到另一個(gè)請(qǐng)求的動(dòng)畫(huà)
另外不同于 Glide v3栈雳,Glide v4 將不會(huì)默認(rèn)應(yīng)用交叉淡入或任何其他的過(guò)渡效果。每個(gè)請(qǐng)求必須手動(dòng)應(yīng)用過(guò)渡缔莲。
動(dòng)畫(huà)的性能提示
Android中的動(dòng)畫(huà)代價(jià)是比較大的哥纫,尤其是同時(shí)開(kāi)始大量動(dòng)畫(huà)的時(shí)候。 交叉淡入和其他涉及 alpha 變化的動(dòng)畫(huà)顯得尤其昂貴痴奏。 此外蛀骇,動(dòng)畫(huà)通常比圖片解碼本身還要耗時(shí)。在列表和網(wǎng)格中濫用動(dòng)畫(huà)可能會(huì)讓圖像的加載顯得緩慢而卡頓读拆。為了提升性能擅憔,請(qǐng)?jiān)谑褂?Glide 向 ListView , GridView, 或 RecyclerView 加載圖片時(shí)考慮避免使用動(dòng)畫(huà),尤其是大多數(shù)情況下檐晕,你希望圖片被盡快緩存和加載的時(shí)候暑诸。作為替代方案,請(qǐng)考慮預(yù)加載辟灰,這樣當(dāng)用戶滑動(dòng)到具體的 item 的時(shí)候个榕,圖片已經(jīng)在內(nèi)存中了。
在多個(gè)請(qǐng)求間交叉淡入
Transitions
并不能讓你在不同請(qǐng)求中加載的兩個(gè)圖像之間做過(guò)渡芥喇。當(dāng)新的加載被應(yīng)用到 View 或 Target (查看 Target的文檔 )上時(shí)西采,Glide 默認(rèn)會(huì)取消任何已經(jīng)存在的請(qǐng)求。因此乃坤,如果你想加載連個(gè)個(gè)不同的圖片并在它們之間做動(dòng)畫(huà)苛让,你無(wú)法直接通過(guò) Glide 來(lái)完成沟蔑。等待第一個(gè)加載完成并在 View 外持有這個(gè) Bitmap 或 Drawable ,然后開(kāi)始新的加載并手動(dòng)在這兩者之間做動(dòng)畫(huà)狱杰,諸如此類的策略看起來(lái)有效瘦材,但是實(shí)際上不安全,并可能導(dǎo)致程序崩潰或圖像錯(cuò)誤仿畸。
相反食棕,最簡(jiǎn)單的辦法是使用包含兩個(gè) ImageView
的 ViewSwitcher
來(lái)完成。將第一張圖片加載到 getNextView()
的返回值里面错沽,然后將第二張圖片加載到 getNextView()
的下一個(gè)返回值中簿晓,并使用一個(gè) RequestListener
在第二張圖片加載完成時(shí)調(diào)用 showNext()
。為了更好地控制千埃,你也可以使用 Android開(kāi)發(fā)者文檔 指出的策略憔儿。但要記住與 ViewSwitcher
一樣,僅在第二次圖像加載完成后才開(kāi)始交叉淡入淡出放可。
配置
在非application
中不能實(shí)現(xiàn)AppGlideModule
內(nèi)存緩存
默認(rèn)情況下谒臼,Glide使用 LruResourceCache
,這是 MemoryCache
接口的一個(gè)缺省實(shí)現(xiàn)耀里,使用固定大小的內(nèi)存和 LRU 算法蜈缤。LruResourceCache
的大小由 Glide 的 MemorySizeCalculator
類來(lái)決定,這個(gè)類主要關(guān)注設(shè)備的內(nèi)存類型冯挎,設(shè)備 RAM 大小底哥,以及屏幕分辨率。
應(yīng)用程序可以自定義 MemoryCache
的大小房官,具體是在它們的 AppGlideModule
中使用 applyOptions(Context, GlideBuilder)
方法配置 MemorySizeCalculator
:
@GlideModule
public class AppGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
MemorySizeCalculator calculator = new MemorySizeCalculator.Builder(context)
.setMemoryCacheScreens(2)
.build();
builder.setMemoryCache(new LruResourceCache(calculator.getMemoryCacheSize()));
}
}
或者
@GlideModule
public class AppGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
int memoryCacheSizeBytes = 1024 * 1024 * 20; // 20mb
builder.setMemoryCache(new LruResourceCache(memoryCacheSizeBytes));
}
}
Bitmap 池
Glide 使用 LruBitmapPool
作為默認(rèn)的 BitmapPool
趾徽。LruBitmapPool
是一個(gè)內(nèi)存中的固定大小的 BitmapPool
,使用 LRU 算法清理翰守。默認(rèn)大小基于設(shè)備的分辨率和密度附较,同時(shí)也考慮內(nèi)存類和 isLowRamDevice
的返回值。具體的計(jì)算通過(guò) Glide 的 MemorySizeCalculator
來(lái)完成潦俺,與 Glide 的 MemoryCache
的大小檢測(cè)方法相似
緩存模式
默認(rèn)情況下Glide會(huì)在開(kāi)始一個(gè)新的圖片請(qǐng)求之前按順序檢查下列各級(jí)緩存
- 活動(dòng)資源 (Active Resources) - 現(xiàn)在是否有另一個(gè) View 正在展示這張圖片拒课?
- 內(nèi)存緩存 (Memory cache) - 該圖片是否最近被加載過(guò)并仍存在于內(nèi)存中?
- 資源類型(Resource) - 該圖片是否之前曾被解碼事示、轉(zhuǎn)換并寫(xiě)入過(guò)磁盤緩存早像?
- 數(shù)據(jù)來(lái)源 (Data) - 構(gòu)建這個(gè)圖片的資源是否之前曾被寫(xiě)入過(guò)文件緩存?
前兩步檢查圖片是否在內(nèi)存中肖爵,如果是則直接返回圖片卢鹦。后兩步則檢查圖片是否在磁盤上,以便快速但異步地返回圖片。
如果四個(gè)步驟都未能找到圖片冀自,則Glide會(huì)返回到原始資源以取回?cái)?shù)據(jù)(原始文件揉稚,Uri, Url等)。
緩存的圖片的key
在 Glide v4 里熬粗,所有緩存鍵都包含至少兩個(gè)元素:
- 請(qǐng)求加載的 model(File, Url, Url)
- 一個(gè)可選的 簽名(Signature)
另外搀玖,步驟1-3(活動(dòng)資源,內(nèi)存緩存驻呐,資源磁盤緩存)的緩存鍵還包含一些其他數(shù)據(jù)灌诅,包括:
- 寬度和高度
- 可選的
變換(Transformation)
- 額外添加的任何
選項(xiàng)(Options)
- 請(qǐng)求的數(shù)據(jù)類型 (Bitmap, GIF, 或其他)
活動(dòng)資源和內(nèi)存緩存使用的鍵還和磁盤資源緩存略有不同,以適應(yīng)內(nèi)存 選項(xiàng)(Options)
含末,比如影響 Bitmap 配置的選項(xiàng)或其他解碼時(shí)才會(huì)用到的參數(shù)猜拾。
為了生成磁盤緩存上的緩存鍵名稱,以上的每個(gè)元素會(huì)被哈嫌逗校化以創(chuàng)建一個(gè)單獨(dú)的字符串鍵名挎袜,并在隨后作為磁盤緩存上的文件名使用。
磁盤緩存策略(Disk Cache Strateg)
默認(rèn)是AUTOMATIC模式,當(dāng)你加載遠(yuǎn)程數(shù)據(jù)(比如肥惭,從URL下載)時(shí)宋雏,AUTOMATIC
策略僅會(huì)存儲(chǔ)未被你的加載過(guò)程修改過(guò)(比如,變換务豺,裁剪–譯者注)的原始數(shù)據(jù),因?yàn)橄螺d遠(yuǎn)程數(shù)據(jù)相比調(diào)整磁盤上已經(jīng)存在的數(shù)據(jù)要昂貴得多嗦明。對(duì)于本地?cái)?shù)據(jù)笼沥,AUTOMATIC
策略則會(huì)僅存儲(chǔ)變換過(guò)的縮略圖,因?yàn)榧词鼓阈枰俅紊闪硪粋€(gè)尺寸或類型的圖片娶牌,取回原始數(shù)據(jù)也很容易奔浅。
修改緩存策略
GlideApp.with(fragment)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
其他的一些api
GlideApp.with(this)
.load("")
.onlyRetrieveFromCache(true) //僅從緩存加載 (省流量模式)
.skipMemoryCache(true) //跳過(guò)緩存,加載原始圖片 (圖片驗(yàn)證碼)
.diskCacheStrategy(DiskCacheStrategy.NONE) //僅跳過(guò)磁盤緩存
.into(imageView);
刷新圖片緩存的方式
GlideApp.with(yourFragment)
.load(yourFileDataModel)
.signature(new ObjectKey(yourVersionMetadata))
.into(yourImageView);
加載圖片的時(shí)候,傳入一個(gè)可變的signature
數(shù)據(jù)來(lái)控制圖片的刷新
清理磁盤
new AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... params) {
// This method must be called on a background thread.
Glide.get(applicationContext).clearDiskCache();
return null;
}
}
注意需要開(kāi)啟子線程,而且需要傳入application
以防止內(nèi)存泄露