- 謹(jǐn)慎使用large heap
android設(shè)備由于軟硬件的差異,heap閥值不同送漠,特殊情況下可以在manifest中使用largeheap=true聲明一個(gè)更大的heap空間,使用getLargeMemoryClass()來(lái)獲取到這個(gè)更大的空間浩姥。但是要謹(jǐn)慎使用脱衙,因?yàn)轭~外的空間會(huì)影響到系統(tǒng)整體的用戶體驗(yàn),并且會(huì)使每次gc的運(yùn)行時(shí)間更長(zhǎng)善炫。切換任務(wù)時(shí)性能大打折扣撩幽,large heap并不一定能獲取到更大的heap.
- 綜合考慮設(shè)備內(nèi)存閾值與其他因素設(shè)計(jì)合適的緩存大小
例如,在設(shè)計(jì)ListView或者GridView的Bitmap LRU緩存的時(shí)候箩艺,需要考慮的點(diǎn)有:
應(yīng)用程序剩下了多少可用的內(nèi)存空間?有多少圖片會(huì)被一次呈現(xiàn)到屏幕上窜醉?有多少圖片需要事先緩存好以便快速滑動(dòng)時(shí)能夠立即顯示到屏幕?
設(shè)備的屏幕大小與密度是多少?
* 一個(gè)xhdpi的設(shè)備會(huì)比hdpi需要一個(gè)更大的Cache來(lái)hold住同樣數(shù)量的圖片舅桩。
* 不同的頁(yè)面針對(duì)Bitmap的設(shè)計(jì)的尺寸與配置是什么酱虎,大概會(huì)花費(fèi)多少內(nèi)存?
* 頁(yè)面圖片被訪問(wèn)的頻率擂涛?是否存在其中的一部分比其他的圖片具有更高的訪問(wèn)頻繁读串?如果是,也許你想要保存那些最常訪問(wèn)的到內(nèi)存中撒妈,或者為不同組別的位圖(按訪問(wèn)頻率分組)設(shè)置多個(gè)LruCache容器恢暖。
- onLowMemory() 與onTrimMemory()
Android可以在不同的應(yīng)用當(dāng)中隨意切換。為了讓background轉(zhuǎn)到foreground, 每一個(gè)background都會(huì)占用一定的內(nèi)存狰右。系統(tǒng)會(huì)根據(jù)內(nèi)存的使用情況決定回收部分background的應(yīng)用內(nèi)存杰捂。background的應(yīng)用從暫停狀態(tài)恢復(fù)到foreground,比較快棋蚌,如果從kill狀態(tài)恢復(fù)比較慢嫁佳。
- 資源文件需要選擇合適的文件夾進(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的目錄下。
- Try catch某些大內(nèi)存分配的操作
在某些情況下蔬充,我們需要事先評(píng)估那些可能發(fā)生OOM的代碼蝶俱,對(duì)于這些可能發(fā)生OOM的代碼,加入catch機(jī)制饥漫,可以考慮在catch里面嘗試一次降級(jí)的內(nèi)存分配操作榨呆。例如decode bitmap的時(shí)候,catch到OOM趾浅,可以嘗試把采樣比例再增加一倍之后愕提,再次嘗試decode。
- 謹(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ì)象证膨。
- 特別留意單例對(duì)象中不合理的持有
雖然單例模式簡(jiǎn)單實(shí)用如输,提供了很多便利性,但是因?yàn)閱卫纳芷诤蛻?yīng)用保持一致央勒,使用不合理很容易出現(xiàn)持有對(duì)象的泄漏不见。
- 珍惜Services資源
如果你的應(yīng)用需要在后臺(tái)使用service,除非它被觸發(fā)并執(zhí)行一個(gè)任務(wù)崔步,否則其他時(shí)候Service都應(yīng)該是停止?fàn)顟B(tài)稳吮。另外需要注意當(dāng)這個(gè)service完成任務(wù)之后因?yàn)橥V箂ervice失敗而引起的內(nèi)存泄漏。 當(dāng)你啟動(dòng)一個(gè)Service井濒,系統(tǒng)會(huì)傾向?yàn)榱吮A暨@個(gè)Service而一直保留Service所在的進(jìn)程灶似。這使得進(jìn)程的運(yùn)行代價(jià)很高,因?yàn)橄到y(tǒng)沒(méi)有辦法把Service所占用的RAM空間騰出來(lái)讓給其他組件瑞你,另外Service還不能被Paged out酪惭。這減少了系統(tǒng)能夠存放到LRU緩存當(dāng)中的進(jìn)程數(shù)量,它會(huì)影響應(yīng)用之間的切換效率者甲,甚至?xí)?dǎo)致系統(tǒng)內(nèi)存使用不穩(wěn)定春感,從而無(wú)法繼續(xù)保持住所有目前正在運(yùn)行的service。 建議使用IntentService虏缸,它會(huì)在處理完交代給它的任務(wù)之后盡快結(jié)束自己鲫懒。更多信息,請(qǐng)閱讀Running in a Background Service刽辙。
- 優(yōu)化布局層次窥岩,減少內(nèi)存消耗
越扁平化的視圖布局,占用的內(nèi)存就越少扫倡,效率越高谦秧。我們需要盡量保證布局足夠扁平化,當(dāng)使用系統(tǒng)提供的View無(wú)法實(shí)現(xiàn)足夠扁平的時(shí)候考慮使用自定義View來(lái)達(dá)到目的撵溃。
- 謹(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)該盡量避免他們。
- 使用nano protobufs序列化數(shù)據(jù)
Protocol buffers是由Google為序列化結(jié)構(gòu)數(shù)據(jù)而設(shè)計(jì)的惶翻,一種語(yǔ)言無(wú)關(guān)姑蓝,平臺(tái)無(wú)關(guān),具有良好的擴(kuò)展性吕粗。類(lèi)似XML纺荧,卻比XML更加輕量,快速颅筋,簡(jiǎn)單宙暇。如果你需要為你的數(shù)據(jù)實(shí)現(xiàn)序列化與協(xié)議化,建議使用nano protobufs议泵。關(guān)于更多細(xì)節(jié)占贫,請(qǐng)參考protobuf readme的”Nano version”章節(jié)。
- 謹(jǐn)慎使用依賴注入框架
使用類(lèi)似Guice或者RoboGuice等框架注入代碼先口,在某種程度上可以簡(jiǎn)化你的代碼型奥。下面是使用RoboGuice前后的對(duì)比圖:
- 謹(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月匣。
- 使用ProGuard來(lái)剔除不需要的代碼
ProGuard能夠通過(guò)移除不需要的代碼,重命名類(lèi)奋姿,域與方法等等對(duì)代碼進(jìn)行壓縮锄开,優(yōu)化與混淆。使用ProGuard可以使得你的代碼更加緊湊称诗,這樣能夠減少mapping代碼所需要的內(nèi)存空間萍悴。
- 謹(jǐn)慎使用第三方libraries
很多開(kāi)源的library代碼都不是為移動(dòng)網(wǎng)絡(luò)環(huán)境而編寫(xiě)的,如果運(yùn)用在移動(dòng)設(shè)備上寓免,并不一定適合癣诱。即使是針對(duì)Android而設(shè)計(jì)的library,也需要特別謹(jǐn)慎袜香,特別是在你不知道引入的library具體做了什么事情的時(shí)候撕予。例如,其中一個(gè)library使用的是nano protobufs, 而另外一個(gè)使用的是micro protobufs蜈首。這樣一來(lái)嗅蔬,在你的應(yīng)用里面就有2種protobuf的實(shí)現(xiàn)方式。這樣類(lèi)似的沖突還可能發(fā)生在輸出日志疾就,加載圖片澜术,緩存等等模塊里面。另外不要為了1個(gè)或者2個(gè)功能而導(dǎo)入整個(gè)library猬腰,如果沒(méi)有一個(gè)合適的庫(kù)與你的需求相吻合鸟废,你應(yīng)該考慮自己去實(shí)現(xiàn),而不是導(dǎo)入一個(gè)大而全的解決方案姑荷。
考慮不同的實(shí)現(xiàn)方式來(lái)優(yōu)化內(nèi)存占用