Android 開發,跳不過的記憶體管理

承香墨影發表於2017-06-28

版權宣告:

本賬號釋出文章均來自公眾號,承香墨影(cxmyDev),版權歸承香墨影所有。

未經允許,不得轉載。

一、前言

在 Android 系統中,當執行的 App 被移動到後臺的之後,Android 為了保證下次啟動的速度,會將它移入 Cached 的狀態,這個時候實際上,該 App 的程式依然存在,但是對應的元件是否存在,就不一定了。而既然該 App 的程式還存活著,下次啟動的速度就會很快,這就是常說的熱啟動。

但是這些被退出到後臺的 App ,也並不是完全安全不會被清理掉的,他們可能只是沒有持有任何的元件,並且是不佔用 CPU 資源的,但是它依然會佔據記憶體空間。而當系統認為記憶體不足的時候,就會按照優先順序清理掉一些優先順序不那麼高的程式,來回收一些記憶體空間,供新啟動的程式使用,這就是 LowMemoryKiller 的策略。

那麼,為了讓我們的 App 在後臺儘可能活的久一點,無非就是將記憶體降低,從而使得優先順序提高,而實現不被系統回收的功能(這是一個常規的優化方案,而非保活方案)。那麼,如果我們能明確當前 App 處於什麼狀態,就能在此時機,釋放一些不需要持有的記憶體資源,來達到我們的目的。

這個時候,就需要用到 onTrimMemory() 這個回撥方法了。

二、什麼是 onTrimMemory

1、onTrimMemory 的作用

前面提到,我們可以通過實現 onTrimMemory() 方法,來完成對當前 App 在記憶體中的優先順序的簡單管理。

onTrimMemory() 回撥方法,是 Android Level 14(Android 4.0) 之後提供的一個 API,它主要的作用是提醒開發者,在系統記憶體不足的時候,應該通過釋放部分不重要的記憶體資源,從而避免被 Android 系統服務殺掉。

可以看到,onTrimMemory() 本質上是一種告知 App 處於系統記憶體回收的不同階段的時機,應該在這些時機,合理對自身持有的記憶體進行釋放,以避免被系統直接殺掉,從而讓保證下次使用者啟動 App 時候的速度。

onTrimMemory() 的完整方法簽名如下:

public void onTrimMemory(int level)複製程式碼

可以看到,它實際上,會有一個 level 引數來標記當前的 App 在記憶體中的級別,也就意味著 onTrimMemory() 方法,可能會被多次呼叫到。

2、 onTrimMemory 回傳的引數的意義

既然 onTrimMemory() 方法會傳遞一個 level 引數,那麼就先來看看,各種 level 引數所代表的含義。

  • TRIM_MEMORY_UI_HIDDEN:App 的所有 UI 介面被隱藏,最常見的就是 App 被 home 鍵或者 back 鍵,置換到後臺了。
  • TRIM_MEMORY_RUNNING_MODERATE:表示 App 正常執行,並且不會被殺掉,但是目前手機記憶體已經有點低了,系統可能會根據 LRU List 來開始殺程式。
  • TRIM_MEMORY_RUNNING_LOW:表示 App正常執行,並且不會被殺掉。但是目前手機記憶體已經非常低了。
  • TRIM_MEMORY_RUNNING_CRITICAL:表示 App 正在正常執行,但是系統已經開始根據 LRU List 的快取規則殺掉了一部分快取的程式。這個時候應該儘可能的釋放掉不需要的記憶體資源,否者系統可能會繼續殺掉其他快取中的程式。
  • TRIM_MEMORY_BACKGROUND:表示 App 退出到後臺,並且已經處於 LRU List 比較靠後的位置,暫時前面還有一些其他的 App 程式,暫時不用擔心被殺掉。
  • TRIM_MENORY_MODERATE:表示 App 退出到後臺,並且已經處於 LRU List 中間的位置,如果手機記憶體仍然不夠的話,還是有被殺掉的風險的。
  • TRIM_MEMORY_COMPLETE:表示 App 退出到後臺,並且已經處於 LRU List 比較考靠前的位置,並且手機記憶體已經極低,隨時都有可能被系統殺掉。

其實從 level 值的取名來看,大致可以分為三類:

  1. UI 置於後臺:TRIM_MEMORY_UI_HIDDEN
  2. App 正在前臺執行時候的狀態:TRIM_MEMORY_RUNNING_Xxx
  3. App 被置於後臺,在 Cached 狀態下的回撥:TRIM_MEMORY_Xxx

這三類中,通常我們只需要關心 App 被置於 Cached 狀態下的情況,因為系統是不會殺掉一個正在前臺執行的 App 的(但可能會觸發 OOM),但是如果該 App 有一些後臺服務正在執行,這個服務也是有被殺的風險的。

而在 Cached 狀態下的時候,當收到 TRIM_MEMORY_Xxx 的回撥,就需要注意了,這些只是標記了當前 App 處於 LRU List 的位置,也就是說,如果回收了靠前的 App 程式之後,依然達不到記憶體使用的要求,可能會進一步去殺程式,也就是說,極端情況下,可能從 TRIM_MEMORY_BACKGROUND 到 TRIM_MEMORY_COMPLETE 是瞬間完成的事情,所以我們需要慎重處理它們,儘量對這三個狀態都進行判斷,然後做統一的回收記憶體資源的處理。

3、哪些元件可以監聽 onTrimMemory

既然說到了 onTrimMemory() 回掉,看樣子它是和 App 相關的,所以最少在 Application 中,應該是可以對其進行重寫來監聽回撥的。但是除了 Application,其他的一些元件中,也是可以監聽它的。

這些可以監聽 onTrimMemory 的元件有:

  • Application
  • Activity
  • Fragment
  • Service
  • ContentProvider

4、自定義 onTrimMemroy 監聽

除了前面提到的系統預設可以監聽 onTrimMemory() 的元件之外,我們還可以自定義 onTrimMemory 的監聽。

自定義起來也非常的簡單,只需要實現 ComponentCallbacks2 介面,然後呼叫 Application.registerComponentCallbacks() 方法註冊即可。

除了 registerComponentCallbacks() 方法進行註冊監聽之外,如果不使用了的話,還可以使用 unregisterComponentCallbacks() 進行解注。

那麼這裡是如何實現的呢?讓我們來看看 Application 的對應原始碼。

可以看到,它實際上是通過一個 mComponentCallbacks 的列表進行維護的。

而在 onTrimMemory() 的時候,又從 mComponentCallbacks 中獲取到所有的 callbacks 物件,進行訊息的分發。

通過這種方式實現了對 onTrimMemory() 的自定義監聽。

onTrimMemory() 方法同時被標記為 @CallSuper,也就嚴格要求了重寫它的子類,必須呼叫父類中的 onTrimMemory() 方法,從而保證了訊息的分發不會缺失。

5、onLowMemory()

onTrimMemory() 既然是 Android 4.0 才新增加的 Api,那麼對於低版本的裝置而言,可以監聽 onLowMemory() 方法,它大概可以等同於 level 級別為 TRIM_MEMORY_COMPLETE 的回撥。

當然,ComponentCallbacks2 介面繼承的 ComponentCallback 介面,也是需要實現 onLowMemory() 方法的。

三、onTrimMemory 的一些思考?

1、為什麼需要 onTrimMemory()

Android 系統會在自身記憶體不足的情況下,清理掉一些不重要的程式來釋放記憶體資源,以供優先順序更高的程式使用。而這個順序,主要是按照 LRU List 中的優先順序來清理的,但是它也同時會考慮清理掉哪些佔用記憶體較高的程式來讓系統更快的釋放跟多的記憶體。

所以,儘可能的讓 App 在系統內,佔用足夠小的記憶體資源,就可以降低被殺的概率,從而下次啟動的時候走熱啟動的方式,提升使用者的體驗。

換一個角度來說,讓 App 佔用較小的記憶體,也可以優化系統的速度,畢竟系統清理程式釋放記憶體的過程,也是需要佔用 CPU 資源的。在大環境下,也是有意義的。

所以,在 onTrimMemory() 的時機,對當前 App 的記憶體進行釋放優化,就尤為重要了。

2、在 onTrimMemory 回撥中,應該釋放哪些資源

onTrimMemory() 回撥中,應該在一些狀態下清理掉不重要的記憶體資源。在不考慮記憶體洩露的情況下,有一些資源是我們主動快取起來,以便我們在使用的過程中可以快速獲取,而這部分資源就是我們清理的重點。

對於這些快取,只要是讀進記憶體內的都算,例如最常見的圖片快取、檔案快取等。拿圖片快取來說,市場上,常規的圖片載入庫,一般而言都是三級快取,所以在記憶體吃緊的時候,我們就應該優先清理掉這部分圖片快取,畢竟圖片是吃記憶體大戶,而且再次回來的時候,雖然記憶體中的資源被回收掉了,我們依然可以從磁碟或者網路上恢復它。

除了資源快取之外,還有一些頁面相關的資源,也是佔據記憶體的,可以考慮清理掉 Activity Task 中,多餘的 Activity,只保留 Root Activity 。

其實核心思想,就是根據 onTrimMemory() 回撥的一些資訊,來釋放我們持有的可被恢復,不那麼重要的記憶體資源,以提高系統效能,已經保證當前 App 的程式不那麼容易被系統回收。

相關文章