天天看點

Google推薦——Glide使用詳解(圖檔加載架構)

本文所使用的glide版本為3.7.0

glide,一個被google所推薦的圖檔加載庫,作者是bumptech。這個庫被廣泛運用在google的開源項目中,包括2014年的google i/o大會上釋出的官方app。(ps:衆所周知的簡介就到此為止了)glide 對于 android sdk 的最低要求是 api level 10glide滑行的意思,可以看出這個庫的主旨就在于讓圖檔加載變的流暢。現在被廣泛使用,當然還是有很多開發者使用square公司的picasso,也有兩個庫的對比原文連結:​​http://inthecheesefactory.com/blog/get-to-know-glide-recommended-by-google/en​​譯文連結:​​http://jcodecraeer.com/a/anzhuokaifa/androidkaifa/2015/0327/2650.html​​

在androidstudio上添加依賴非常簡單

glide 也支援 maven 項目形式:

github位址:https://github.com/bumptech/glide     如果是eclipse使用去下載下傳glide的jar在項目中使用就可以了,jar的連結​​https://github.com/bumptech/glide/releases​​

glide的一個完整的請求至少需要三個參數,代碼如下:

with(context context) - 需要上下文,這裡還可以使用 activity、fragmentactivity、android.support.v4.app.fragment、android.app.fragment 的對象。将 activity/fragment 對象作為參數的好處是,圖檔的加載會和 activity/fragment 的生命周期保持一緻,例如:onpaused 時暫停加載,onresume 時又會自動重新加載。是以在傳參的時候建議使用 activity/fragment 對象,而不是 context。

load(string url) - 這裡我們所使用的一個字元串形式的網絡圖檔的 url,後面會講解 load() 的更多使用方式

into(imageview imageview) - 你需要顯示圖檔的目标 imageview

偶爾出現圖檔加載慢或者加載不出來的情況是難以避免的,是以為了 ui 能好看一些,我們會使用占位圖。glide 也為我們提供這種方法 placeholder() 和 error()

注:這裡需要注意一點,placeholder() 和 error() 的參數都是隻支援 int 和 drawable 類型的參數,這種設計應該是考慮到使用本地圖檔比網絡圖檔更加合适做占位圖。

glide 的縮略圖功能在這裡不得不說,和占位圖略有不同,占位圖必須使用資源檔案才行,而縮略圖是動态的占位圖可以從網絡中加載。縮略圖會在世紀請求加載完成或者處理完之後才顯示。在原始圖檔到達之後,縮略圖不會取代原始圖檔,隻會被抹除。glide 為縮略圖提供了2種不同的加載方式,比較簡單的方式是調用 thumbnail() 方法,參數是 float 類型,作為其倍數大小。例如,你傳入 0.2f 作為參數,glide 将會顯示原始圖檔的20%的大小,如果原圖是 1000x1000 的尺寸,那麼縮略圖将會是 200x200 的尺寸。為縮略圖明顯比原圖小得多,是以我們需要確定 imageview 的 scaletype 設定的正确。

注:應用于請求的設定也将應用于縮略圖。

使用 thumbnail() 方法來設定是簡單粗暴的,但是如果縮略圖需要通過網絡加載相同的全尺寸圖檔,就不會很快的顯示了。是以 glide 提供了另一種防止去加載縮略圖,先看代碼

與第一種方式不同的是,這裡的第一個縮略圖請求是完全獨立于第二個原始請求的。該縮略圖可以是不同的資源圖檔,同時也可以對縮略圖做不同的轉換,等等...

動畫效果可以讓圖檔加載變得更加的平滑,crossfade() 方法強制開啟 glide 預設的圖檔淡出淡入動畫,目前版本3.7.0是預設開啟的。crossfade() 還有一個重載方法 crossfade(int duration)。可以控制動畫的持續時間,機關ms。動畫預設的持續時間是300ms。既然可以添加動畫,那肯定就可以設定沒有任何淡出淡入效果,調用 dontanimate()

ps:glide 是可以自定義動畫效果的,這個在後面會講解

在項目開發過程中,指定圖檔顯示大小長長可能用到,畢竟從伺服器擷取的圖檔不一定都是符合設計圖的标準的。我們在這裡就可以使用 override(width,height) 方法,在圖檔顯示到 imageview 之前,重新改變圖檔大小。

在設定圖檔到 imageview 的時候,為了避免圖檔被擠壓失真,imageview 本身提供了 scaletype 屬性,這個屬性可以控制圖檔顯示時的方式,具體的屬性使用還是去搜尋吧!glide 也提供了兩個類似的方法 centercrop() 和 fitcenter(),centercrop() 方法是将圖檔按比例縮放到足矣填充 imageview 的尺寸,但是圖檔可能會顯示不完整;而 fitcenter() 則是圖檔縮放到小于等于 imageview 的尺寸,這樣圖檔是顯示完整了,但是 imageview 就可能不會填滿了。

注:其實 glide 的 centercrop() 和 fitcenter() 這兩個方法分别對應 imageview 的 scaletype 屬性中的 center_crop 和 fit_center 命名基本一緻。

為了更快的加載圖檔,我們肯定希望可以直接拿到圖檔,而不是進行網絡請求,是以我們需要緩存。glide 通過使用預設的記憶體和磁盤緩存來避免不必要的網絡請求,之後我們再詳細的去看它的實作。

記憶體緩存是 glide 預設幫我們做了的,除非你不需要,可以調用 skipmemorycache(true) 告訴 glide 跳過記憶體緩存。這樣 glide 就不會把這張圖檔放到記憶體緩存中,該方法隻影響記憶體緩存。(不要問調用skipmemorycache(false)的問題,glide 是預設将圖檔放入記憶體緩存中的)

磁盤緩存

磁盤緩存也是預設開啟的,當然也是可以關閉的,不過關閉的方式略微有點不一樣。

上面這段代碼将記憶體緩存和磁盤緩存都禁用了,這裡使用枚舉 diskcachestrategy.none 将磁盤緩存禁用了,這裡涉及到了自定義磁盤緩存行為,我們接下來就講解這個。

自定義磁盤緩存行為

使用 diskcachestrategy 可以為 glide 配置磁盤緩存行為。glide 的磁盤緩存比較複雜,這也是在圖檔加載可以比 picasso 的原因(之一)。picasso 隻緩存了全尺寸的圖檔,而 glide 的不同之處在于,glide 不僅緩存了全尺寸的圖,還會根據 imageview 大小所生成的圖也會緩存起來。比如,請求一個 800x600 的圖加載到一個 400x300 的 imageview 中,glide預設會将這原圖還有加載到 imageview 中的 400x300 的圖也會緩存起來。

diskcachestrategy 的枚舉意義: diskcachestrategy.none 什麼都不緩存 diskcachestrategy.source 隻緩存全尺寸圖 diskcachestrategy.result 隻緩存最終的加載圖 diskcachestrategy.all 緩存所有版本圖(預設行為)

這隻是舉個例子而已

同一時間加載多個圖檔,app 将難以避免這種情況。如果這個時候我們希望使用者的體驗更好,往往會選擇先加載對于使用者更加重要的圖檔。glide 可以調用 .priority() 方法配合 priority 枚舉來設定圖檔加載的優先級。

priority.low priority.normal priority.high priority.immediat 這裡有一點需要注意,優先級并不是完全嚴格遵守的。glide 将會用他們作為一個準則,盡可能的處理這些請求,但是不能保證所有的圖檔都會按照所有要求的順序加載。

顯示 gif 對于 glide 來說一個比較特别的功能(至少 picasso 暫時還不行)而且使用起來非常簡單

這段代碼還有點問題,如果加載的不是一張 gif 圖的話,是沒有辦法顯示的。

做以上修改,如果圖檔類型不是 gif 圖的話就會當作 load 失敗來處理,是以 error() 會被回調。即使這個url的圖檔是好的,也是不會顯示的。當然,如果你想顯示 gif 但隻是向現實靜态的圖檔你就可以這麼做

僅僅是顯示 gif 的第一幀圖像,這樣就可以保證圖檔的正常顯示了。還有一個神奇的功能,glide 還能顯示視訊!but...隻能夠顯示手機本地的視訊,要是向現實網絡上的視訊的話,還是另尋他法吧!

glide 的基礎使用就講解到這了。

到現在為止,我們所涉及到的代碼都是直接加載圖檔到 imageview 中。glide 隐藏做了所有的網絡請求和背景的線程處理,圖檔準備好之後切回到 ui 線程重新整理 imageview。也就是說 imageview 在我們代碼的鍊式結構中成為了最後一步,但是如果我們需要擷取到 bitmap 本身

的話我們就需要用到 target 了。target 其實就是整個圖檔的加載的生命周期,是以我們就可以通過它在圖檔加載完成之後擷取到 bitmap。

其實對于 target 可以簡單的了解為回調,本身就是一個 interface,glide本身也為我們提供了很多 target
Google推薦——Glide使用詳解(圖檔加載架構)

所有targets

simpletarget

直接上代碼

首先建立了一個 simpletarget 的對象并且實作了 onresourceready() 方法,看方法名能知道是圖檔加載完之後會調用該方法,參數就有我們需要的 bitmap 。而使用 simpletarget 的對象的時候就像使用 imageview 一樣,作為參數傳給 into() 方法就行了,glide 會内部去處理并傳回結果給任何一個對象。這裡我們為了防止加載 gif 、 video 或者一些位置資源時與 msimpletarget 沖突,是以我們調用了 asbitmap() 方法,使其隻能傳回 bitmap 對象。這裡就有個問題了,如果我需要改變圖檔的大小怎麼辦?這點小問題 glide 還是有考慮到的,加入原尺寸 1000x1000 的圖檔,我們顯示的時候隻需要是 500x500 的尺寸來節省時間和記憶體,你可以在 simpletarget 的回調聲明中指定圖檔的大小。

從代碼中可以看到 simpletarget 的對象的聲明沒有使用匿名對象,而是單獨的聲明了一個變量,這裡是故意這麼做的,如果使用匿名内部類的方式建立 simpletarget 的對象,這樣會增大該對象在 glide 完成圖檔請求之前就被回收的可能性。還記得前面說過 with() 方法傳入 activity 或者 fragment 時 glide 的圖檔加載會與他們的生命周期關聯起來,但是如果我們使用 target 的話,這個 target 就有可能獨立于他們的生命周期以外,這時候我們就需要使用 context.getapplicationcontext() 的上下文了,這樣隻有在應用完全停止時 glide 才會殺死這個圖檔請求。代碼如下

viewtarget

當我們使用 custom view 時,glide 并不支援加載圖檔到自定義 view 中的,使用 viewtarget 更容易實作。

上面這個例子就沒有辦法直接使用 .into() ,如果我們使用 viewtarget 實作呢!

在 target 的 onresourceready 回調方法中使用自定義 view 自己的方法去設定圖檔,可以看到在建立 viewtarget 的時候傳入了 customview 的對象。還有其他target的使用這裡就不一一講述了,例如 appwidgettarget 、 notificationtarget ...

圖檔顯示之前我們可能還需要對圖檔進行處理操作,比如:圖檔切圓角,灰階處理等等;這些需求我們通過 transformations 操作 bitmap 來實作,我們可以修改圖檔的任意屬性:尺寸,範圍,顔色,像素位置等等。其實我們之前已經提到過兩個 transformation 了,即 fitcenter 和 centercrop ,這兩個是 glide 已經實作的。接下來就要講講怎麼樣來實作自己的 transformation ,我們需要建立一個類去實作 transformation 接口,但是要實作這個方法還是比較複雜的,接口中 transform 方法提供的參數 resource<t> resource 不是那麼好處理的。如果你隻是想要對圖檔(不是 gif 和 video)做正常的 bitmap 轉換,我們推薦你使用抽象類 bitmaptransformation。它簡化了很多的實作,這應該能覆寫 95% 的應用場景啦。下面的代碼實作了對圖檔切圓角的操作,其中 getid() 方法描述了這個 transformation 的唯一辨別,為避免意外我們需要確定它是唯一的。

現在我們有了自己的 transformation 就可以來看看怎麼使用了。調用 .transform() 方法,将自定義的 transformation 的對象作為參數傳遞進去就可以使用你的 transformation 了,這裡也可以使用 .bitmaotransform() 但是它隻能用于 bitmap 的轉換。

如果我們需要同時執行多個 transformation 的話,我們不能使用鍊式的形式多次調用 .transform() 或 .bitmaptransform() 方法,即使你調用了,之前的配置就會被覆寫掉!我們可以直接傳遞多個轉換對象給 .transform() 或 .bitmaptransform() 。

這段代碼中我們把一個圖檔切圓角,然後做了順時針旋轉90度處理。下面是旋轉處理的代碼

注:這裡需要注意一點 .centercrop() 和 .fitcenter() 也都是 transformation 是以也是遵循同時使用多個 transformation 的規則的,即:當你使用了自定義轉換後你就不能使用 .centercrop() 或 .fitcenter() 了。

這裡有一個 glide transformations 的庫,它提供了很多 transformation 的實作,非常值得去看,不必重複造輪子對吧!

​​glide-transformations​​

這個庫有兩個不同的版本,擴充版本包含了更多的 transformation ,它是通過裝置的 gpu 來計算處理的,需要有額外的依賴,是以這兩個版本的設定有一點不同。還是根據需要再決定使用那個版本吧!

從圖像到圖像的平滑過渡是非常重要,glide 中有一個标準動畫去柔軟的在你的 ui 中改變,但是我們現在希望設定自己的動畫。

這是個 xml 動畫縮放動畫,圖檔剛開始小的,然後逐漸增大到原尺寸。我們現在要應用到 glide 加載圖檔中去,調用 .animate() 方法傳入 xml 動畫的 id 即可。

這種加載方式用在正常的 imageview 上是沒有問題的,但如果使用的 target 是一些自定義的時候就沒法好好的實作了。這時候我們就可以通過傳入實作了 viewpropertyanimation.animator 接口的類對象來實作。

然後,我們隻需要在 glide 請求中設定這個動畫對象就ok了

在 animate(view view) 中你的動畫對象方法中, 你可以做任何你想要對視圖做的事情。自由的用你建立的動畫吧。

glide 的 module 是一個可以全局改變 glide 的行為的東西,為了定制 glide 的行為我們要去實作 interface glidemodule 來寫我們自己的代碼。

可以看到 glidemodule 為我們提供了兩個方法,這裡我們主要使用的是 applyoptions(context context, glidebuilder builder) , 我們自己的需要重新定義的代碼寫在該方法裡就可以了。然後我們還需要去 androidmanifest.xml 中使用 meta 聲明我們上面實作的 module

到這裡我們就完成了 examplemodule 的聲明,glide 将會在工作是使用我們所定義的 module

tips 我們需要将 android:name 屬性改成 包名+類名 的形式,這樣的引用才是正确的。如果你想删掉 glide module,隻需要删除在 androidmanifest.xml 中的聲明就可以了。java 類可以儲存,說不定以後會用呢。如果它沒有在 androidmanifest.xml 中被引用,那它不會被加載或被使用。 定制 module 的話 glide 會有這樣一個優點:你可以同時聲明多個 glide module。glide 将會(沒有特定順序)得到所有的聲明 module。因為你目前不能定義順序,請確定定制不會引起沖突!

這個過程走通了,接下來我們來看看是怎麼自定義的。applyoptions(context context, glidebuilder builder) 中有兩個參數, 我們通過使用 glidebuilder 來實作我們的需求。先看看 glidebuilder 中可用的方法

.setmemorycache(memorycache memorycache)

.setbitmappool(bitmappool bitmappool)

.setdiskcache(diskcache.factory diskcachefactory)

.setdiskcacheservice(executorservice service)

.setresizeservice(executorservice service)

.setdecodeformat(decodeformat decodeformat)

可以看到,這個 glidebuilder 對象給你通路了 glide 重要的核心元件。接下來我們就要試着去使用這些方法

增加 glide 的圖檔品質

在 android 中有兩個主要的方法對圖檔進行解碼:argb_8888 和 rgb_565 。前者為每個像素使用4個位元組,後者每個像素僅使用2個位元組。argb_8888 的有時就是圖像品質更高以及能儲存一個 alpha 通道。 picasso 使用的就是 argb_8888 , glide 預設使用低品質的 rgb_565 ,但是現在你就可以使用 glide module 來改變圖檔解碼規則。就象這樣

這樣我們就簡單的增加了 glide 的圖檔品質。往往我們還會遇到一些情況,希望 glide 可以使用我們自己的網絡架構,我們就需要做一些事情來實作這個需求了。glide 的開發者不強制設定網絡庫給你,是以glide可以說和 https 無關。理論上,它可以與任何的網絡庫實作,隻要覆寫了基本的網絡能力就行。同樣是需要實作 glide 的 moduleloader 的接口,為了讓我們更加易用,glide 為 okhttp 和 volley 兩個網絡庫提供了實作。假設我要內建 okhttp 作為 glide 的網絡庫,我可以手動實作一個 glidemodule 也可以在 build.gradle 中添加依賴:

gradle 會自動合并必要的 glidemodule 到你的 androidmanifest.xml , glide 會認可在 manifest 中存在,然後使用 okhttp 做到的所有網絡連接配接。