Glide記憶體處理最佳化

來源:互聯網
上載者:User
一、前言

Glide 是 Google 官方推薦的一款圖片載入庫,使用起來也非常的簡單便利,Glide 它幫我們完成了很多很重要,但是卻通用的功

能,例如:圖片的載入壓縮、展示、載入圖片的記憶體管理等等。

對 Glide 還不熟悉的朋友,可以參考 《一篇好文,助你上手 Glide》

但是,在使用 Glide 的時候,有一些小技巧,可以讓你的記憶體更最佳化,避免可能出現的 OOM。例如:雖然 Glide 會根據載入的控制項

大小,最佳化載入後的圖片尺寸,可如果載入的是一張全屏的大圖,依然會是一個佔用記憶體空間非常大的操作。

具體一張 Bitmap 到底佔用了多少記憶體空間,可以參考《Bitmap 比你想的更費記憶體 | 吊打 OOM》

本文有些建議來自 Android TV App,而 Android TV 眾多的智能電視和智能盒子,實際上硬體條件非常的惡劣,而 Android T

V 的 App ,為了美化,會用到大部分的圖片,所以在圖片使用方面,OOM 的問題就會被放大,而下面介紹的一些最佳化方

案,在 Android 手機硬體條件非常好的環境下,不使用影響也不大。 二、開始最佳化 2.1 配置好 TrimMemory 和 LowMemory

Glide 幫我們做了大部分記憶體管理方面的事情,實際上它還支援做的更好。

對於一個 App 而言,在系統記憶體環境不足的情況下,會回調一些 onTrimMemory() 或者 onLowMemory() 等方法,這些都是在提醒開

發者,當前裝置的記憶體環境已經發生了變化,你最好調整你的記憶體使用量策略,避免被系統清理掉或者出現 OOM 。

關於 onTrimMemroy() 相關內容,不瞭解的可以先參考《Android 開發,跳不過的記憶體管理》

而 Glide 也為我們提供了類似方法的介面,開發人員只需要調用即可,它在內部會隨著不同的記憶體情況,幫我們對緩衝的圖片進行最佳化。

在這裡,你主要用到 Glide 的 trimMemory() 和 cleanMemroy() 方法,它們一個用來裁剪 Glide 緩衝的圖片記憶體空間,一個用來清

理 Glide 緩衝的記憶體空間。

在使用 onTrimMemory() 之前,一般是實現 ComponentCallbacks2 介面,然後在 Application 中,通過 registerComponentCallback

s() 方法進行註冊。當然,如果你嫌麻煩,還可以直接在 Application 中,重寫對應的方法。

瞭解了這些,就可以根據我們的需要來配置在何時調用 Glide 的對應方法,我推薦的配置: 在 lowMemory 的時候,調用 Glide.cleanMemroy() 清理掉所有的記憶體緩衝。 在 App 被置換到背景時候,調用 Glide.cleanMemroy() 清理掉所有的記憶體緩衝。 在其它情況的 onTrimMemroy() 回調中,直接調用 Glide.trimMemory() 方法來交給 Glide 處理記憶體情況。

那麼對應的代碼,如下:

既然知道需要調用 Glide 的這兩個方法,我們還是需要瞭解到它內部到底幫我們做了什麼。先來看看 Glide 對應的源碼。

在 Glide 的這些方法內,可以看到,它們都會去操作 memoryCache 和 bitmapPool 這兩個對象,實際上它們是兩個介面,這裡如果做特殊處理,操作

的都是 Glide 對它們的預設實現,LruResourceCache 和 LruBitmapPool 。從名稱上可以看出來,它們都是遵循 Lru 演算法的。

就 Glide 而言,Memory Cache 是 Glide 用來在記憶體中緩衝圖片資源,使其在需要使用的時候立刻就可以使用,而不必執行磁

盤的 I/O 操作,而 BitmatPool 則是 Glide 維護了一個圖片複用池,LruBitmapPool 使用 Lru 演算法保留最近使用的尺寸的 Bitma

p,這不是本文的重點,大家瞭解一下即可。

其實 LruResourceCache 和 LruBitmapPool 中,對 clearMemory() 和 trimMemory() 的操作是類似的,這裡就以 LruBitmapPool 舉

例。

在 LruBitmapPool 中,會根據回調的方法以及參數,調用 clearMemory() 或者 trimToSize(),其實最終都是調用的 trimToSiz

e() 方法。它用於裁剪當前緩衝資源的個數。

可以看到,根據裁剪的目尺規寸,會去回收多餘的 Bitmap 到合適的目標大小,以達到清理記憶體的目的。 2.2 配置 GlideModule

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

具體 GlideModule 的使用,可以參見官方文檔:

https://github.com/bumptech/glide/wiki/Configuration

GlideModule 是一個介面,需要實現其對應的方法。

這裡我們只需要使用 applyOptions() 這個方法,它用於在 Glide 的預設配置的基礎上,追加一些我們需要的配置。

而在這裡,我們可以根據當前裝置的記憶體情況,對其進行一個設定,使用 ActivityManager 擷取當前裝置的記憶體情況,如果是處於 lo

wMemory 的時候,將圖片的 DecodeFormat 設定為 RGB_565 , RGB_565 和預設的 ARGB_8888 比,每個像素會少 2 個byte,這

樣,等於一張同樣的圖片,載入到記憶體中會少一半記憶體的佔用(ARGB_8888 每個像素占 4 byte)。

2.3 避免使用圓角的ImageView

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

果。

而在 Android 下,也有大量的類似 XxxImageView 的開原始檔控制,用於操作 Bitmap 以達到一個圓角圖片的效果,例如 Github 上比較火

的 RoundedImageView。

它們大部分的原理,是接收到你傳遞的 Bitmap ,然後再輸出一個與原來 Bitmap 等大的新 Bitmap ,在此基礎之上,進行圓角的一

些處理,這就導致了,實際上會在記憶體中,多持有一個 Bitmap ,一下一張圖片佔用的記憶體就被加倍了。

所以既然已經選擇使用 Glide ,推薦使用 glide-transformations 這個開源庫配合使用,glide-transformations 利用 Glide 的 bitmapTra

nsfrom() 介面,實現對載入的 Bitmap 的進行一些變換操作。

glide-transformations 的 Github 地址如下:

https://github.com/wasabeef/glide-transformations

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 ,來重新指定載入圖片的

尺寸。

既然 Glide 已經提供了標準的 Api ,那麼我們還需要擷取到當前運行裝置的寬高。

這裡推薦使用 getRealSize() 的方式擷取螢幕的寬高,它可以真實的拿到當前螢幕的尺寸。其它 Api 在部分智能電視和盒子上,拿

到的尺寸會小,因為沒有計算 StatusBar 或者 NavigationBar的高度,這些都是經驗之談。

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

在其中,記錄 trim 的 level 這個值,反應當前的記憶體層級,在使用的時候,通過 getBitmapSize() 裁剪出一個符合當前記憶體環境的

尺寸。

例子中只是對 TRIM_MENORY_RUNNING_LOW 進行了處理,會根據螢幕尺寸,縮放到 0.8f 倍的狀態。如果要做的更多,可以將其

它幾個 level 也加上,調整不同的縮放倍數。

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

I/cxmyDev: bgImage byteCount : 8294400I/cxmyDev: bgImage byteCount : 5308416
1 2

可以看到,最佳化的目的還是達到了。可以節約大概 3MB 左右的記憶體空間,而圖片又不至於模糊到無法看的地步。 三、小結

最佳化是沒有終點的,今天先聊到這裡,之後有想到的再補充。如果你有什麼更好的建議,可以在文末留言一起討論一下。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.