Android圖片加載(二)——Glide框架的引入和使用指南

前言

上一講Android圖片加載(一)——框架的對比分析中我們提到了列表控件在快速滑動或者是網(wǎng)絡(luò)不好的時候塔次,可能會出現(xiàn)圖片錯位幔嗦、重復(fù)猾警、閃爍等問題,為了使用戶擁有良好的App使用體驗褒墨,不僅要快速加載圖片,而且還不能有過多的主線程I/O或頻繁的垃圾回收。因此,我們引入了圖片加載框架Glide县习。

一、簡介

Glide是一個被google所推薦的圖片加載庫谆趾,作者是bumptech躁愿。這個庫被廣泛運用在google的開源項目中,包括2014年的google I/O大會上發(fā)布的官方app沪蓬。
其特點包括但不限于:

  1. 使用簡明的流式語法API彤钟,它允許你在大部分情況下一行代碼搞定需求。
    例如:
  Glide.with(fragment)
    .load(url)
    .into(imageView);
  1. 性能好怜跑,充分考慮了Android圖片加載性能的兩個關(guān)鍵方面:圖片解碼的速度样勃、解碼圖片帶來的資源壓力吠勘。
  2. 可以自動、智能地下采樣和緩存峡眶, 以最小化存儲開銷和解碼次數(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. 占位圖
  1. 顧名思義,占位圖就是指在圖片的加載過程中踊兜,我們先顯示一張臨時的圖片竿滨,等圖片加載出來了之后再替換成要加載的圖片。
Glide.with(this)
     .load(url)
     .placeholder(R.drawable.loading) //
     .into(imageView);
  1. 除了加載占位圖之外捏境,還有一種異常占位圖于游。異常占位圖是指,如果因為某些異常情況導(dǎo)致圖片加載失敗垫言,比如說手機(jī)網(wǎng)絡(luò)信號不好贰剥,這個時候就顯示這張異常占位圖。
    一般通過串接error()方法來指定異常占位圖筷频。
  2. 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è)計出于以下兩個目的:

  1. 集成庫可以為Generated API擴(kuò)展自定義選項涨冀。
  2. 在Appplication模塊中可以將常用的選項組打包成一個選項在Gnenrated API中使用。

我在代碼中引入了Generated API主要出于目的2麦萤,雖然可以通過手動創(chuàng)建RequestOptions子類的方式來完成鹿鳖,但是這種用法降低了API使用的流暢性,如果使用 Generated API 壮莹,可以使我們不用去創(chuàng)建 RequestOptions 就可以直接使用 placeholder翅帜、error 等方法。簡單理解一下就是增加代碼復(fù)用行命满,便于后續(xù)的代碼維護(hù)涝滴。
下面通過實際的代碼來展示Generated API的使用:

  1. 使用Generated API必須首先引入下列依賴:
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
  1. 在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的注意事項

  1. 在4.0中不用像3.X需要在AndroidManifest.xml配置GlideModule场钉,而是通過注解繼承AppGlideModule的子類來配置蚊俺。

  2. 使用GlideApp代替Glide,asBitmap逛万、asGif泳猬、asDrawable、asFile都要放到load之前(glide3.7.0都是要在load之后調(diào)用)宇植。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末得封,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子指郁,更是在濱河造成了極大的恐慌忙上,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件闲坎,死亡現(xiàn)場離奇詭異疫粥,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)腰懂,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門梗逮,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人绣溜,你說我怎么就攤上這事慷彤。” “怎么了?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵瞬欧,是天一觀的道長贷屎。 經(jīng)常有香客問我,道長艘虎,這世上最難降的妖魔是什么唉侄? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮野建,結(jié)果婚禮上属划,老公的妹妹穿的比我還像新娘。我一直安慰自己候生,他們只是感情好同眯,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唯鸭,像睡著了一般须蜗。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上目溉,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天明肮,我揣著相機(jī)與錄音,去河邊找鬼缭付。 笑死柿估,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的陷猫。 我是一名探鬼主播秫舌,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绣檬!你這毒婦竟也來了足陨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤娇未,失蹤者是張志新(化名)和其女友劉穎墨缘,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忘蟹,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡飒房,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年搁凸,在試婚紗的時候發(fā)現(xiàn)自己被綠了媚值。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡护糖,死狀恐怖褥芒,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤锰扶,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布献酗,位于F島的核電站,受9級特大地震影響坷牛,放射性物質(zhì)發(fā)生泄漏罕偎。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一京闰、第九天 我趴在偏房一處隱蔽的房頂上張望颜及。 院中可真熱鬧,春花似錦蹂楣、人聲如沸俏站。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肄扎。三九已至,卻和暖如春赁酝,著一層夾襖步出監(jiān)牢的瞬間犯祠,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工赞哗, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留雷则,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓肪笋,卻偏偏與公主長得像月劈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子藤乙,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容