前言
上一講Android圖片加載(一)——框架的對比分析中我們提到了列表控件在快速滑動或者是網(wǎng)絡(luò)不好的時候塔次,可能會出現(xiàn)圖片錯位幔嗦、重復(fù)猾警、閃爍等問題,為了使用戶擁有良好的App使用體驗褒墨,不僅要快速加載圖片,而且還不能有過多的主線程I/O或頻繁的垃圾回收。因此,我們引入了圖片加載框架Glide县习。
一、簡介
Glide是一個被google所推薦的圖片加載庫谆趾,作者是bumptech躁愿。這個庫被廣泛運用在google的開源項目中,包括2014年的google I/O大會上發(fā)布的官方app沪蓬。
其特點包括但不限于:
- 使用簡明的流式語法API彤钟,它允許你在大部分情況下一行代碼搞定需求。
例如:
Glide.with(fragment)
.load(url)
.into(imageView);
- 性能好怜跑,充分考慮了Android圖片加載性能的兩個關(guān)鍵方面:圖片解碼的速度样勃、解碼圖片帶來的資源壓力吠勘。
- 可以自動、智能地下采樣和緩存峡眶, 以最小化存儲開銷和解碼次數(shù)剧防。能夠積極的資源重用,例如字節(jié)數(shù)組和Bitmap對象辫樱,以最小化垃圾回收和堆碎片的影響峭拘。包含深度的生命周期集成,以確保僅優(yōu)先處理活躍的Fragment和Activity的請求狮暑,有利于應(yīng)用在必要時釋放資源以避免在后臺時被殺掉鸡挠。
2. 使用
1. 添加gradle依賴
dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
2. 基本方法
例如加載一個地址為url的圖片到uImageView中:
Glide.with(uContext).load(url).into(uImageView);
其中,uContext
是上下文搬男,這里還可以使用Activity拣展、FragmentActivity、androidx.Fragment.app.Fragment等對象缔逛。with方法中傳入的實例會決定Glide加載圖片的生命周期备埃,如果傳入的是Activity或者Fragment的實例,那么當(dāng)這個Activity或Fragment被銷毀的時候褐奴,圖片加載也會停止按脚。如果傳入ApplicationContext,那么只有當(dāng)應(yīng)用程序被殺掉的時候敦冬,圖片加載才會停止辅搬。
此外,也可以通過clear取消不必要的加載脖旱,但是這并不是必須的操作堪遂,實際上,當(dāng)Glide.with()中傳入的Activity或者是Fragment實例被銷毀時萌庆,Glide會自動取消加載并回收資源蚤氏。
3. 占位圖
- 顧名思義,占位圖就是指在圖片的加載過程中踊兜,我們先顯示一張臨時的圖片竿滨,等圖片加載出來了之后再替換成要加載的圖片。
Glide.with(this)
.load(url)
.placeholder(R.drawable.loading) //
.into(imageView);
- 除了加載占位圖之外捏境,還有一種異常占位圖于游。異常占位圖是指,如果因為某些異常情況導(dǎo)致圖片加載失敗垫言,比如說手機(jī)網(wǎng)絡(luò)信號不好贰剥,這個時候就顯示這張異常占位圖。
一般通過串接error()方法來指定異常占位圖筷频。 - null占位圖
正常情況下蚌成,我們的 load() 方法里面的 url 應(yīng)該不是 null 的前痘,但是如果有可能為 null 的情況,你可以通過設(shè)置 fallback() 方法來顯示 url 為 null 的情況担忧。
4. 加載指定大小的圖片
我們平時在加載圖片的時候很容易會造成內(nèi)存浪費芹缔。比如說一張圖片的尺寸是1000X1000像素,但是我們界面上的ImageView可能只有200X200像素瓶盛,這個時候如果你不對圖片進(jìn)行任何壓縮就直接讀取到內(nèi)存中最欠,這就屬于內(nèi)存浪費了,因為程序中根本就用不到這么高像素的圖片惩猫。
而使用Glide芝硬,我們完全不用擔(dān)心圖片內(nèi)存浪費,甚至是內(nèi)存溢出的問題轧房。因為Glide從來都不會直接將圖片的完整尺寸全部加載到內(nèi)存中拌阴,而是用多少加載多少。Glide會自動判斷ImageView的大小奶镶,然后只將這么大的圖片像素加載到內(nèi)存當(dāng)中(默認(rèn)情況下)皮官,幫助我們節(jié)省內(nèi)存開支。所以使用Glide实辑,在絕大多數(shù)情況下我們都是不需要指定圖片大小的,因為Glide會自動根據(jù)ImageView的大小來指定圖片的大小藻丢。
不過Glide仍然支持給圖片指定一個固定的大小的功能(使用override)剪撬,指定后不管imagview的大小是多少,Glide只會講圖片加載成設(shè)定的尺寸悠反。此外残黑,如果不想讓Glide計算并壓縮要加載的圖片,要加載原始圖片的大小斋否,可以使用override(Target.SIZE_ORIGINAL,Target.SIZE_ORIGINAL)作為options添加上去梨水,但是這種加載方式是不被建議的,因為占用的內(nèi)存會相差很大茵臭。
5. 加載不同格式:Bitmap疫诽、Gif、Drawable旦委、File
在 Glide4.0 中有一個 RequestBuilders 的泛型類奇徒,用于指定加載資源的格式,可以通過下面四種方法指定缨硝,得到不同的 RequestBuilders 對象:
- asDrawable() 得到 RequestBuilders
- asGif() 得到 RequestBuilders
- asBitmap() 得到 RequestBuilders
- asFile() 得到 RequestBuilders
默認(rèn)情況下摩钙,如果我們不指定,則是得到一個 RequestBuilders 對象查辩。
例如胖笛,代碼中使用:
Glide.with(this)
.asBitmap()
.load(url)
.into(userAvatarImageView);
表示加載bitmap
6. 換一種方式加載圖片
正常情況下网持,我們都是通過這行代碼來加載圖片的:
Glide.with(uContext).load(url).into(uImageView);
但是有時候,為了對加載出來的圖像進(jìn)行一些處理长踊,我們還可以使用Glide為我們提供的一個封裝功舀,封裝成一個Target或者Target的子類,例如SimpleTarget之斯。
Glide.with(this)
.asBitmap()
.load(loginInfo.getAvatarUrl())
.into(new SimpleTarget<Bitmap>() {
@Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
userAvatarImageView.setImageBitmap(resource);
LoginUser.bitmap=resource;
}});
我們使用SimpleTarget可以將Glide加載出來的圖片對象獲取到并做一些處理日杈,而不是像之前那樣只能顯示圖片。比如說佑刷,這里獲取到加載出來的圖片對象之后不僅將它顯示到一個ImageView當(dāng)中莉擒,還將它存儲到LoginUser.bitmap中作為一個靜態(tài)變量,方便后續(xù)頁面的使用瘫絮。
7. 使用Generated API
Generated API模式的設(shè)計出于以下兩個目的:
- 集成庫可以為Generated API擴(kuò)展自定義選項涨冀。
- 在Appplication模塊中可以將常用的選項組打包成一個選項在Gnenrated API中使用。
我在代碼中引入了Generated API主要出于目的2麦萤,雖然可以通過手動創(chuàng)建RequestOptions子類的方式來完成鹿鳖,但是這種用法降低了API使用的流暢性,如果使用 Generated API 壮莹,可以使我們不用去創(chuàng)建 RequestOptions 就可以直接使用 placeholder翅帜、error 等方法。簡單理解一下就是增加代碼復(fù)用行命满,便于后續(xù)的代碼維護(hù)涝滴。
下面通過實際的代碼來展示Generated API的使用:
- 使用Generated API必須首先引入下列依賴:
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
- 在Application模塊下創(chuàng)建一個新的類繼承自GlideModule,并給類添加@GlideModule注解胶台。
@GlideModule
public class MyAppGlideModule extends AppGlideModule {
@Override
public void applyOptions(Context context, GlideBuilder builder) {
builder.setDefaultRequestOptions(
new RequestOptions()
.diskCacheStrategy(DiskCacheStrategy.RESOURCE)
.placeholder(R.drawable.logo)
.fallback(R.drawable.fail)
.format(DecodeFormat.PREFER_ARGB_8888));
}
}
例如歼疮,上面的MyAppGlideModule中我們使用applyOptions的方法配置了一些options,Generated API默認(rèn)名字為GlideApp, 把原來的Glide.with替換成GlideApp.with诈唬,就可以使用該API去完成加載工作韩脏。
8. 集成網(wǎng)絡(luò)框架
Glide默認(rèn)使用的是HTTPUrlConnection,支持集成Volley,Okhttp等網(wǎng)絡(luò)棧铸磅。
為了使用比Android提供的HttpUrlConnection更nice的API赡矢,以及想要確保網(wǎng)絡(luò)層代碼不依賴于app所在的設(shè)備上Android OS版本,所以選擇Okhttp阅仔。其次济竹,我們已經(jīng)在獲取遠(yuǎn)程github數(shù)據(jù)的時候使用了Okhttp,那么我們繼續(xù)選擇為Glide使用OKhttp霎槐。
具體使用
添加一個對OKhttp集成庫的依賴:compile "com.github.bumptech.glide:okhttp3-integration:4.11.0"
添加Okhttp集成庫的Gradle依賴將使Glide自動開始使用Okhttp來加載所有來自http和httpsURL的圖片送浊。
9. 圖片的緩存處理
Glide的緩存包含內(nèi)存緩存和硬盤緩存。默認(rèn)情況下丘跌,Glide自動就是開啟內(nèi)存緩存和磁盤緩存的袭景。內(nèi)存緩存的主要作用是防止應(yīng)用重復(fù)將圖片數(shù)據(jù)讀取到內(nèi)存當(dāng)中唁桩,而硬盤緩存的主要作用是防止應(yīng)用從網(wǎng)絡(luò)或其他地方重復(fù)下載和讀取數(shù)據(jù)。(??這兩句話難道不是一個意思嗎)
9.1 內(nèi)存緩存
- 默認(rèn)情況下耸棒,Glide自動就是開啟內(nèi)存緩存的荒澡。也就是說,當(dāng)我們使用Glide加載了一張圖片之后与殃,這張圖片就會被緩存到內(nèi)存當(dāng)中单山,只要在它還沒從內(nèi)存中被清除之前,下次使用Glide再加載這張圖片都會直接從內(nèi)存當(dāng)中讀取幅疼,而不用重新從網(wǎng)絡(luò)或硬盤上讀取了米奸,這樣無疑就可以大幅度提升圖片的加載效率。比方說你在一個RecyclerView當(dāng)中反復(fù)上下滑動爽篷,RecyclerView中只要是Glide加載過的圖片都可以直接從內(nèi)存當(dāng)中迅速讀取并展示出來悴晰,從而大大提升了用戶體驗。還可以使用skipMemoryCache()方法并傳入true逐工,就表示禁用掉Glide的內(nèi)存緩存功能铡溪。
- 從內(nèi)存緩存中讀取圖片的邏輯大致是:如果能從內(nèi)存緩存當(dāng)中讀取到要加載的圖片,那么就直接進(jìn)行回調(diào)泪喊,如果讀取不到的話棕硫,才會開啟線程執(zhí)行后面的圖片加載邏輯。
- Glide的圖片加載過程會調(diào)用兩個方法來獲取內(nèi)存緩存袒啼,LoadFromCache()和LoadFromActiveResource()哈扮。這兩個方法中一個使用的就是LruCache算法,另一個使用的就是弱引用瘤泪。activeResources就是一個弱引用的HashMap,用來緩存正在使用中的圖片育八,使用activeResources來緩存正在使用中的圖片对途,可以保護(hù)這些圖片不會被LruCache算法回收掉∷杵澹總結(jié):正在使用中的圖片使用弱引用來進(jìn)行緩存实檀,不在使用中的圖片使用LruCache來進(jìn)行緩存。
9.2 硬盤緩存
- 當(dāng)我們使用Glide去加載一張圖片的時候按声,Glide默認(rèn)并不會將原始圖片展示出來膳犹,而是會對圖片進(jìn)行壓縮和轉(zhuǎn)換。Glide默認(rèn)情況下在硬盤緩存的就是轉(zhuǎn)換過后的圖片签则。
- 可以通過.diskCacheStrategy來設(shè)置磁盤的緩存策略
包括下面這些:
//DiskCacheStrategy.ALL 既緩存原始圖片须床,也緩存轉(zhuǎn)換過后的圖片。
// DiskCacheStrategy.NONE 不緩存任何內(nèi)容
// DiskCacheStrategy.DATA 在資源解碼前就將原始數(shù)據(jù)寫入磁盤緩存(即只緩存原始圖片)
// DiskCacheStrategy.RESOURCE 在資源解碼后將數(shù)據(jù)寫入磁盤緩存渐裂,即經(jīng)過縮放等轉(zhuǎn)換后的圖片資源(即只緩存轉(zhuǎn)換過后的圖片)霞赫。
// DiskCacheStrategy.AUTOMATIC 讓Glide根據(jù)圖片資源智能地選擇使用哪一種緩存策略鹏浅。
//默認(rèn)采用DiskCacheStrategy.AUTOMATIC策略
/*-------------------------------------------------------------------------------*/
//源碼 RequestOptions.java
private DiskCacheStrategy diskCacheStrategy = DiskCacheStrategy.AUTOMATIC;
三秕狰、從源碼的角度理解Glide的執(zhí)行流程
上面介紹的使用過程中實際上有小部分已經(jīng)討論到了Glide的源碼,但是由于Glide的源碼比較復(fù)雜篓跛,對于筆者這種菜雞來說是比較難以自己下手的,這里引用一下《第一行代碼》作者郭霖大神的原話:
閱讀源碼到底困難嗎坦刀?這個當(dāng)然主要還是要視具體的源碼而定愧沟。比如同樣是圖片加載框架,我讀Volley的源碼時就感覺酣暢淋漓鲤遥,并且對Volley的架構(gòu)設(shè)計和代碼質(zhì)量深感佩服沐寺。讀Glide的源碼時卻讓我相當(dāng)痛苦,代碼極其難懂渴频。當(dāng)然這里我并不是說Glide的代碼寫得不好芽丹,只是因為Glide和復(fù)雜程度和Volley完全不是在一個量級上的。
但是郭霖大神仍然給出了一套更容易被理解的源碼解析卜朗,感興趣的同學(xué)可以去看:
Android圖片加載框架最全解析(二)拔第,從源碼的角度理解Glide的執(zhí)行流程
四、Glide4.0的注意事項
在4.0中不用像3.X需要在AndroidManifest.xml配置GlideModule场钉,而是通過注解繼承AppGlideModule的子類來配置蚊俺。
使用GlideApp代替Glide,asBitmap逛万、asGif泳猬、asDrawable、asFile都要放到load之前(glide3.7.0都是要在load之后調(diào)用)宇植。