Android如何避免OOM總結(jié)

前面介紹了一些基礎(chǔ)的內(nèi)存管理機(jī)制以及OOM的基礎(chǔ)知識(shí),那么在實(shí)踐操作當(dāng)中荚虚,有哪些指導(dǎo)性的規(guī)則可以參考呢低剔?歸納下來(lái)速梗,可以從四個(gè)方面著手肮塞,首先是減小對(duì)象的內(nèi)存占用,其次是內(nèi)存對(duì)象的重復(fù)利用姻锁,然后是避免對(duì)象的內(nèi)存泄露枕赵,最后是內(nèi)存使用策略優(yōu)化。

1)使用更加輕量的數(shù)據(jù)結(jié)構(gòu)

例如位隶,我們可以考慮使用ArrayMap/SparseArray而不是HashMap等傳統(tǒng)數(shù)據(jù)結(jié)構(gòu)拷窜,下圖演示了HashMap的簡(jiǎn)要工作原理,相比起Android系統(tǒng)專(zhuān)門(mén)為移動(dòng)操作系統(tǒng)編寫(xiě)的ArrayMap容器钓试,在大多數(shù)情況下装黑,都顯示效率低下,更占內(nèi)存弓熏。通常的HashMap的實(shí)現(xiàn)方式更加消耗內(nèi)存恋谭,因?yàn)樗枰粋€(gè)額外的實(shí)例對(duì)象來(lái)記錄Mapping操作。另外挽鞠,SparseArray更加高效在于他們避免了對(duì)key與value的autobox自動(dòng)裝箱疚颊,并且避免了裝箱后的解箱。

2)避免在Android里面使用Enum

3)減小Bitmap對(duì)象的內(nèi)存占用

Bitmap是一個(gè)極容易消耗內(nèi)存的大胖子信认,減小創(chuàng)建出來(lái)的Bitmap的內(nèi)存占用是很重要的材义,通常來(lái)說(shuō)有下面2個(gè)措施:

inSampleSize:縮放比例,在把圖片載入內(nèi)存之前嫁赏,我們需要先計(jì)算出一個(gè)合適的縮放比例其掂,避免不必要的大圖載入。

decode format:解碼格式潦蝇,選擇ARGB_8888/RBG_565/ARGB_4444/ALPHA_8款熬,存在很大差異。

4)使用更小的圖片

在設(shè)計(jì)給到資源圖片的時(shí)候攘乒,我們需要特別留意這張圖片是否存在可以壓縮的空間贤牛,是否可以使用一張更小的圖片。盡量使用更小的圖片不僅僅可以減少內(nèi)存的使用则酝,還可以避免出現(xiàn)大量的InflationException殉簸。假設(shè)有一張很大的圖片被XML文件直接引用,很有可能在初始化視圖的時(shí)候就會(huì)因?yàn)閮?nèi)存不足而發(fā)生InflationException沽讹,這個(gè)問(wèn)題的根本原因其實(shí)是發(fā)生了OOM般卑。

在Android上面最常用的一個(gè)緩存算法是LRU(Least Recently Use)

5)復(fù)用系統(tǒng)自帶的資源

Android系統(tǒng)本身內(nèi)置了很多的資源,例如字符串/顏色/圖片/動(dòng)畫(huà)/樣式以及簡(jiǎn)單布局等等妥泉,這些資源都可以在應(yīng)用程序中直接引用椭微。這樣做不僅僅可以減少應(yīng)用程序的自身負(fù)重,減小APK的大小盲链,另外還可以一定程度上減少內(nèi)存的開(kāi)銷(xiāo)蝇率,復(fù)用性更好迟杂。但是也有必要留意Android系統(tǒng)的版本差異性,對(duì)那些不同系統(tǒng)版本上表現(xiàn)存在很大差異本慕,不符合需求的情況排拷,還是需要應(yīng)用程序自身內(nèi)置進(jìn)去。

6)注意在ListView/GridView等出現(xiàn)大量重復(fù)子組件的視圖里面對(duì)ConvertView的復(fù)用

7)Bitmap對(duì)象的復(fù)用

在ListView與GridView等顯示大量圖片的控件里面需要使用LRU的機(jī)制來(lái)緩存處理好的Bitmap锅尘。

利用inBitmap的高級(jí)特性提高Android系統(tǒng)在Bitmap分配與釋放執(zhí)行效率上的提升(3.0以及4.4以后存在一些使用限制上的差異)监氢。使用inBitmap屬性可以告知Bitmap解碼器去嘗試使用已經(jīng)存在的內(nèi)存區(qū)域,新解碼的bitmap會(huì)嘗試去使用之前那張bitmap在heap中所占據(jù)的pixel data內(nèi)存區(qū)域藤违,而不是去問(wèn)內(nèi)存重新申請(qǐng)一塊區(qū)域來(lái)存放bitmap浪腐。利用這種特性,即使是上千張的圖片顿乒,也只會(huì)僅僅只需要占用屏幕所能夠顯示的圖片數(shù)量的內(nèi)存大小议街。

8)避免在onDraw方法里面執(zhí)行對(duì)象的創(chuàng)建

類(lèi)似onDraw等頻繁調(diào)用的方法,一定需要注意避免在這里做創(chuàng)建對(duì)象的操作璧榄,因?yàn)樗麜?huì)迅速增加內(nèi)存的使用特漩,而且很容易引起頻繁的gc,甚至是內(nèi)存抖動(dòng)骨杂。

9)StringBuilder

在有些時(shí)候涂身,代碼中會(huì)需要使用到大量的字符串拼接的操作,這種時(shí)候有必要考慮使用StringBuilder來(lái)替代頻繁的“+”搓蚪。

避免對(duì)象的內(nèi)存泄露

內(nèi)存對(duì)象的泄漏蛤售,會(huì)導(dǎo)致一些不再使用的對(duì)象無(wú)法及時(shí)釋放,這樣一方面占用了寶貴的內(nèi)存空間妒潭,很容易導(dǎo)致后續(xù)需要分配內(nèi)存的時(shí)候悍抑,空閑空間不足而出現(xiàn)OOM。顯然杜耙,這還使得每級(jí)Generation的內(nèi)存區(qū)域可用空間變小,gc就會(huì)更容易被觸發(fā)拂盯,容易出現(xiàn)內(nèi)存抖動(dòng)佑女,從而引起性能問(wèn)題。

10)注意Activity的泄漏

通常來(lái)說(shuō)谈竿,Activity的泄漏是內(nèi)存泄漏里面最嚴(yán)重的問(wèn)題团驱,它占用的內(nèi)存多,影響面廣空凸,我們需要特別注意以下兩種情況導(dǎo)致的Activity泄漏:

內(nèi)部類(lèi)引用導(dǎo)致Activity的泄漏

最典型的場(chǎng)景是Handler導(dǎo)致的Activity泄漏嚎花,如果Handler中有延遲的任務(wù)或者是等待執(zhí)行的任務(wù)隊(duì)列過(guò)長(zhǎng),都有可能因?yàn)镠andler繼續(xù)執(zhí)行而導(dǎo)致Activity發(fā)生泄漏呀洲。此時(shí)的引用關(guān)系鏈?zhǔn)荓ooper -> MessageQueue -> Message -> Handler -> Activity紊选。為了解決這個(gè)問(wèn)題啼止,可以在UI退出之前,執(zhí)行remove Handler消息隊(duì)列中的消息與runnable對(duì)象兵罢∠追常或者是使用Static + WeakReference的方式來(lái)達(dá)到斷開(kāi)Handler與Activity之間存在引用關(guān)系的目的。

Activity Context被傳遞到其他實(shí)例中卖词,這可能導(dǎo)致自身被引用而發(fā)生泄漏巩那。

內(nèi)部類(lèi)引起的泄漏不僅僅會(huì)發(fā)生在Activity上,其他任何內(nèi)部類(lèi)出現(xiàn)的地方此蜈,都需要特別留意即横!我們可以考慮盡量使用static類(lèi)型的內(nèi)部類(lèi),同時(shí)使用WeakReference的機(jī)制來(lái)避免因?yàn)榛ハ嘁枚霈F(xiàn)的泄露裆赵。

11)考慮使用Application Context而不是Activity Context

對(duì)于大部分非必須使用Activity Context的情況(Dialog的Context就必須是Activity Context)东囚,我們都可以考慮使用Application Context而不是Activity的Context,這樣可以避免不經(jīng)意的Activity泄露顾瞪。

12)注意臨時(shí)Bitmap對(duì)象的及時(shí)回收

雖然在大多數(shù)情況下舔庶,我們會(huì)對(duì)Bitmap增加緩存機(jī)制,但是在某些時(shí)候陈醒,部分Bitmap是需要及時(shí)回收的惕橙。例如臨時(shí)創(chuàng)建的某個(gè)相對(duì)比較大的bitmap對(duì)象,在經(jīng)過(guò)變換得到新的bitmap對(duì)象之后钉跷,應(yīng)該盡快回收原始的bitmap弥鹦,這樣能夠更快釋放原始bitmap所占用的空間。

需要特別留意的是Bitmap類(lèi)里面提供的createBitmap()方法:

這個(gè)函數(shù)返回的bitmap有可能和source bitmap是同一個(gè)爷辙,在回收的時(shí)候彬坏,需要特別檢查source bitmap與return bitmap的引用是否相同,只有在不等的情況下膝晾,才能夠執(zhí)行source bitmap的recycle方法栓始。

13)注意WebView的泄漏

Android中的WebView存在很大的兼容性問(wèn)題,不僅僅是Android系統(tǒng)版本的不同對(duì)WebView產(chǎn)生很大的差異血当,另外不同的廠商出貨的ROM里面WebView也存在著很大的差異幻赚。更嚴(yán)重的是標(biāo)準(zhǔn)的WebView存在內(nèi)存泄露的問(wèn)題,看這里WebView causes memory leak - leaks the parent Activity臊旭。所以通常根治這個(gè)問(wèn)題的辦法是為WebView開(kāi)啟另外一個(gè)進(jìn)程落恼,通過(guò)AIDL與主進(jìn)程進(jìn)行通信,WebView所在的進(jìn)程可以根據(jù)業(yè)務(wù)的需要選擇合適的時(shí)機(jī)進(jìn)行銷(xiāo)毀离熏,從而達(dá)到內(nèi)存的完整釋放佳谦。

14)資源文件需要選擇合適的文件夾進(jìn)行存放

我們知道hdpi/xhdpi/xxhdpi等等不同dpi的文件夾下的圖片在不同的設(shè)備上會(huì)經(jīng)過(guò)scale的處理。例如我們只在hdpi的目錄下放置了一張100100的圖片滋戳,那么根據(jù)換算關(guān)系钻蔑,xxhdpi的手機(jī)去引用那張圖片就會(huì)被拉伸到200200啥刻。需要注意到在這種情況下,內(nèi)存占用是會(huì)顯著提高的矢棚。對(duì)于不希望被拉伸的圖片郑什,需要放到assets或者nodpi的目錄下。

15)謹(jǐn)慎使用static對(duì)象

因?yàn)閟tatic的生命周期過(guò)長(zhǎng)蒲肋,和應(yīng)用的進(jìn)程保持一致蘑拯,使用不當(dāng)很可能導(dǎo)致對(duì)象泄漏,在Android中應(yīng)該謹(jǐn)慎使用static對(duì)象兜粘。

16)特別留意單例對(duì)象中不合理的持有

雖然單例模式簡(jiǎn)單實(shí)用申窘,提供了很多便利性,但是因?yàn)閱卫纳芷诤蛻?yīng)用保持一致孔轴,使用不合理很容易出現(xiàn)持有對(duì)象的泄漏剃法。

17)珍惜Services資源

18)優(yōu)化布局層次,減少內(nèi)存消耗

越扁平化的視圖布局路鹰,占用的內(nèi)存就越少贷洲,效率越高。我們需要盡量保證布局足夠扁平化晋柱,當(dāng)使用系統(tǒng)提供的View無(wú)法實(shí)現(xiàn)足夠扁平的時(shí)候考慮使用自定義View來(lái)達(dá)到目的优构。

19)謹(jǐn)慎使用“抽象”編程

很多時(shí)候,開(kāi)發(fā)者會(huì)使用抽象類(lèi)作為”好的編程實(shí)踐”雁竞,因?yàn)槌橄竽軌蛱嵘a的靈活性與可維護(hù)性钦椭。然而,抽象會(huì)導(dǎo)致一個(gè)顯著的額外內(nèi)存開(kāi)銷(xiāo):他們需要同等量的代碼用于可執(zhí)行碑诉,那些代碼會(huì)被mapping到內(nèi)存中彪腔,因此如果你的抽象沒(méi)有顯著的提升效率,應(yīng)該盡量避免他們进栽。

20)謹(jǐn)慎使用多進(jìn)程

使用多進(jìn)程可以把應(yīng)用中的部分組件運(yùn)行在單獨(dú)的進(jìn)程當(dāng)中德挣,這樣可以擴(kuò)大應(yīng)用的內(nèi)存占用范圍,但是這個(gè)技術(shù)必須謹(jǐn)慎使用快毛,絕大多數(shù)應(yīng)用都不應(yīng)該貿(mào)然使用多進(jìn)程盲厌,一方面是因?yàn)槭褂枚噙M(jìn)程會(huì)使得代碼邏輯更加復(fù)雜,另外如果使用不當(dāng)祸泪,它可能反而會(huì)導(dǎo)致顯著增加內(nèi)存。當(dāng)你的應(yīng)用需要運(yùn)行一個(gè)常駐后臺(tái)的任務(wù)建芙,而且這個(gè)任務(wù)并不輕量没隘,可以考慮使用這個(gè)技術(shù)。

一個(gè)典型的例子是創(chuàng)建一個(gè)可以長(zhǎng)時(shí)間后臺(tái)播放的Music Player禁荸。如果整個(gè)應(yīng)用都運(yùn)行在一個(gè)進(jìn)程中右蒲,當(dāng)后臺(tái)播放的時(shí)候阀湿,前臺(tái)的那些UI資源也沒(méi)有辦法得到釋放。類(lèi)似這樣的應(yīng)用可以切分成2個(gè)進(jìn)程:一個(gè)用來(lái)操作UI瑰妄,另外一個(gè)給后臺(tái)的Service陷嘴。

做好內(nèi)存優(yōu)化是一項(xiàng)長(zhǎng)期的工作, 需要在很多地方注意间坐,且行且珍惜灾挨!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市竹宋,隨后出現(xiàn)的幾起案子劳澄,更是在濱河造成了極大的恐慌,老刑警劉巖蜈七,帶你破解...
    沈念sama閱讀 206,214評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件秒拔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡飒硅,警方通過(guò)查閱死者的電腦和手機(jī)砂缩,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)三娩,“玉大人庵芭,你說(shuō)我怎么就攤上這事【∽兀” “怎么了喳挑?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,543評(píng)論 0 341
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)滔悉。 經(jīng)常有香客問(wèn)我伊诵,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,221評(píng)論 1 279
  • 正文 為了忘掉前任居兆,我火速辦了婚禮芽腾,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘笛坦。我一直安慰自己,他們只是感情好苔巨,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,224評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布版扩。 她就那樣靜靜地躺著,像睡著了一般侄泽。 火紅的嫁衣襯著肌膚如雪礁芦。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,007評(píng)論 1 284
  • 那天,我揣著相機(jī)與錄音柿扣,去河邊找鬼肖方。 笑死,一個(gè)胖子當(dāng)著我的面吹牛未状,可吹牛的內(nèi)容都是我干的俯画。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼司草,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼艰垂!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起翻伺,我...
    開(kāi)封第一講書(shū)人閱讀 36,956評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤材泄,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后吨岭,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體拉宗,經(jīng)...
    沈念sama閱讀 43,441評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,925評(píng)論 2 323
  • 正文 我和宋清朗相戀三年辣辫,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旦事。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,018評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡急灭,死狀恐怖姐浮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情葬馋,我是刑警寧澤卖鲤,帶...
    沈念sama閱讀 33,685評(píng)論 4 322
  • 正文 年R本政府宣布,位于F島的核電站畴嘶,受9級(jí)特大地震影響蛋逾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窗悯,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,234評(píng)論 3 307
  • 文/蒙蒙 一区匣、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧蒋院,春花似錦亏钩、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,240評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至辞友,卻和暖如春栅哀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,464評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工昌屉, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人茵瀑。 一個(gè)月前我還...
    沈念sama閱讀 45,467評(píng)論 2 352
  • 正文 我出身青樓间驮,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親马昨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子竞帽,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,762評(píng)論 2 345

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