Glide 這樣用,更省內存!!!

1、前言

Glide 是 Google 官方推薦的一款圖片加載庫,使用起來也很是的簡單便利,Glide 它幫咱們完成了不少很重要,可是卻通用的功能,例如:圖片的加載壓縮、展現、加載圖片的內存管理等等。git

對 Glide 還不熟悉的朋友,能夠參考 《一篇好文,助你上手 Glidegithub

可是,在使用 Glide 的時候,有一些小技巧,可讓你的內存更優化,避免可能出現的 OOM。例如:雖然 Glide 會根據加載的控件大小,優化加載後的圖片尺寸,可若是加載的是一張全屏的大圖,依然會是一個佔用內存空間很是大的操做。算法

具體一張 Bitmap 到底佔用了多少內存空間,能夠參考《Bitmap 比你想的更費內存 | 吊打 OOM緩存

本文有些建議來自 Android TV App,而 Android TV 衆多的智能電視和智能盒子,實際上硬件條件很是的惡劣,而 Android TV 的 App ,爲了美化,會用到大部分的圖片,因此在圖片使用方面,OOM 的問題就會被放大,而下面介紹的一些優化方案,在 Android 手機硬件條件很是好的環境下,不使用影響也不大。app

2、開始優化

2.1 配置好 TrimMemory 和 LowMemory

Glide 幫咱們作了大部份內存管理方面的事情,實際上它還支持作的更好。ide

對於一個 App 而言,在系統內存環境不足的狀況下,會回調一些 onTrimMemory() 或者 onLowMemory() 等方法,這些都是在提醒開發者,當前設備的內存環境已經發生了變化,你最好調整你的內存使用策略,避免被系統清理掉或者出現 OOM 。優化

關於 onTrimMemroy() 相關內容,不瞭解的能夠先參考《Android 開發,跳不過的內存管理ui

而 Glide 也爲咱們提供了相似方法的接口,開發者只須要調用便可,它在內部會隨着不一樣的內存狀況,幫咱們對緩存的圖片進行優化。spa

在這裏,你主要用到 Glide 的 trimMemory()cleanMemroy() 方法,它們一個用來裁剪 Glide 緩存的圖片內存空間,一個用來清理 Glide 緩存的內存空間。3d

在使用 onTrimMemory() 以前,通常是實現 ComponentCallbacks2 接口,而後在 Application 中,經過 registerComponentCallbacks() 方法進行註冊。固然,若是你嫌麻煩,還能夠直接在 Application 中,重寫對應的方法。

瞭解了這些,就能夠根據咱們的須要來配置在什麼時候調用 Glide 的對應方法,我推薦的配置:

  1. 在 lowMemory 的時候,調用 Glide.cleanMemroy() 清理掉全部的內存緩存。
  2. 在 App 被置換到後臺的時候,調用 Glide.cleanMemroy() 清理掉全部的內存緩存。
  3. 在其它狀況的 onTrimMemroy() 回調中,直接調用 Glide.trimMemory() 方法來交給 Glide 處理內存狀況。

那麼對應的代碼,以下:

/gm-app.png
/gm-app.png

既然知道須要調用 Glide 的這兩個方法,咱們仍是須要了解到它內部到底幫咱們作了什麼。先來看看 Glide 對應的源碼。

/gm-trim.png
/gm-trim.png

在 Glide 的這些方法內,能夠看到,它們都會去操做 memoryCachebitmapPool 這兩個對象,實際上它們是兩個接口,這裏若是作特殊處理,操做的都是 Glide 對它們的默認實現,LruResourceCacheLruBitmapPool 。從名稱上能夠看出來,它們都是遵循 Lru 算法的。

就 Glide 而言,Memory Cache 是 Glide 用來在內存中緩存圖片資源,使其在須要使用的時候馬上就可使用,而沒必要執行磁盤的 I/O 操做,而 BitmatPool 則是 Glide 維護了一個圖片複用池,LruBitmapPool 使用 Lru 算法保留最近使用的尺寸的 Bitmap,這不是本文的重點,你們瞭解一下便可。

其實 LruResourceCache 和 LruBitmapPool 中,對 clearMemory()trimMemory() 的操做是相似的,這裏就以 LruBitmapPool 舉例。

/gm-LruBitmapPool.png
/gm-LruBitmapPool.png

在 LruBitmapPool 中,會根據回調的方法以及參數,調用 clearMemory() 或者 trimToSize(),其實最終都是調用的 trimToSize() 方法。它用於裁剪當前緩存資源的個數。

/gm-trimtosize.png
/gm-trimtosize.png

能夠看到,根據裁剪的目標尺寸,會去回收多餘的 Bitmap 到合適的目標大小,以達到清理內存的目的。

2.2 配置 GlideModule

GlideModule 是 Glide 提供的一個配置接口,它會在第一次使用 Glide 的時候被調用,用於進行 Glide 的一些初始的配置。

具體 GlideModule 的使用,能夠參見官方文檔:

github.com/bumptech/gl…

GlideModule 是一個接口,須要實現其對應的方法。

/gm-module.png
/gm-module.png

這裏咱們只須要使用 applyOptions() 這個方法,它用於在 Glide 的默認配置的基礎上,追加一些咱們須要的配置。

而在這裏,咱們能夠根據當前設備的內存狀況,對其進行一個設定,使用 ActivityManager 獲取當前設備的內存狀況,若是是處於 lowMemory 的時候,將圖片的 DecodeFormat 設置爲 RGB_565RGB_565 和默認的 ARGB_8888 比,每一個像素會少 2 個byte,這樣,等於一張一樣的圖片,加載到內存中會少一半內存的佔用(ARGB_8888 每一個像素佔 4 byte)。

/gm-apply.png
/gm-apply.png

2.3 避免使用圓角的ImageView

在實際項目內,常常會用到一些帶圓角的圖片,或者直接就是圓形的圖片。圓形的圖片,多數用於一些用戶的頭像之類的顯示效果。

而在 Android 下,也有大量的相似 XxxImageView 的開源控件,用於操做 Bitmap 以達到一個圓角圖片的效果,例如 Github 上比較火的 RoundedImageView

它們大部分的原理,是接收到你傳遞的 Bitmap ,而後再輸出一個與原來 Bitmap 等大的新 Bitmap ,在此基礎之上,進行圓角的一些處理,這就致使了,實際上會在內存中,多持有一個 Bitmap ,一下一張圖片佔用的內存就被加倍了。

因此既然已經選擇使用 Glide ,推薦使用 glide-transformations 這個開源庫配合使用,glide-transformations 利用 Glide 的 bitmapTransfrom() 接口,實現對加載的 Bitmap 的進行一些變換操做。

glide-transformations 的 Github 地址以下:

github.com/wasabeef/gl…

glide-transformations 提供一系類對加載的圖片的變換操做,從形狀變換到色彩變換,所有支持,基本上知足大部分開發須要,而且它會複用 Glide 的 BitmapPool ,來達到節約內存的目的。

具體 glide-transformations 的使用,能夠查看 Github 上的文檔,下面是它的一個效果圖。

2.4 根據內存狀況,裁剪你的圖片

前面的介紹的一些優化點,都是一些推薦的通用作法,基本上用了前面介紹的辦法,圖片致使的 OOM 應該會大幅度減小。

接下來介紹一個在 Android TV 上,加載全屏大圖的時候,優化內存問題的一個解決辦法。

首先要明確一點,國內 Android TV 的硬件環境很是的很差,二百三百的智能盒子處處都在賣,畢竟也是跑的 Android 系統,你想一想你使用的是一款 299 的 Android 手機,你對它也不會有什麼期待了。可是 Android TV 又是爲了電視作的,因此大部分狀況下,它都是須要支持 1920 * 1280 之類的屏幕尺寸,致使它若是加載一張全屏的大圖,消耗的內存是不忍直視的,若是在內存環境很差的狀況下,可能就直接 OOM 崩潰了。

因此,對於這種極端的狀況,我想到了一個辦法,根據當前的內存環境,按比例縮小須要顯示的全屏圖片,這樣加載到內存中的圖片,就是按比例縮小的。

在這裏就須要用到 DrawableRequestBuilder 的 override() 這個 Api 了,它能夠接受一個 width 和 height ,來從新指定加載圖片的尺寸。

/gm-overr.png
/gm-overr.png

既然 Glide 已經提供了標準的 Api ,那麼咱們還須要獲取到當前運行設備的寬高。

/gm-display.png
/gm-display.png

這裏推薦使用 getRealSize() 的方式獲取屏幕的寬高,它能夠真實的拿到當前屏幕的尺寸。其它 Api 在部分智能電視和盒子上,拿到的尺寸會小,由於沒有計算 StatusBar 或者 NavigationBar的高度,這些都是經驗之談。

同時,咱們也須要用到 ComponentCallbacks2 這個接口,前面已經介紹過了,就再也不贅述了。

/gm-listener.png
/gm-listener.png

在其中,記錄 trim 的 level 這個值,反應當前的內存級別,在使用的時候,經過 getBitmapSize() 裁剪出一個符合當前內存環境的尺寸。

例子中只是對 TRIM_MENORY_RUNNING_LOW 進行了處理,會根據屏幕尺寸,縮放到 0.8f 倍的狀態。若是要作的更多,能夠將其它幾個 level 也加上,調整不一樣的縮放倍數。

/gm-fullscreenload.png
/gm-fullscreenload.png

兩個都輸出一下,看看差異,同一張全屏的圖片,不縮放和縮放 0.8f 的差異。

I/cxmyDev: bgImage byteCount : 8294400
I/cxmyDev: bgImage byteCount : 5308416複製代碼

能夠看到,優化的目的仍是達到了。能夠節約大概 3MB 左右的內存空間,而圖片又不至於模糊到沒法看的地步。

3、小結

優化是沒有終點的,今天先聊到這裏,以後有想到的再補充。若是你有什麼更好的建議,能夠在文末留言一塊兒討論一下。

公衆號二維碼.jpg
公衆號二維碼.jpg

點贊或者分享吧~

相關文章
相關標籤/搜索