相信一步步走過來的Android從業(yè)者,每個人都會遇到OOM的情況。如何避免和防范OOM的出現(xiàn)个从,對于每一個程序員來說確實是一門必不可少的能力。今天我們就談談在Android平臺下內(nèi)存的管理之道,開始今天的主題之前嗦锐,先再次回顧兩個概念嫌松。
內(nèi)存泄漏:對象在內(nèi)存heap堆中中分配的空間,當不再使用或沒有引用指向的情況下奕污,仍不能被GC正澄幔回收的情況。多數(shù)出現(xiàn)在不合理的編碼情況下碳默,比如在 Activity中注冊了一個廣播接收器贾陷,但是在頁面關閉的時候進行unRegister,就會出現(xiàn)內(nèi)存溢出的現(xiàn)象嘱根。通常情況下髓废,大量的內(nèi)存泄漏會造成 OOM。
OOM:即OutOfMemoery该抒,顧名思義就是指內(nèi)存溢出了慌洪。內(nèi)存溢出是指APP向系統(tǒng)申請超過最大閥值的內(nèi)存請求,系統(tǒng)不會再分配多余的空間凑保,就會造成OOM error冈爹。在我們Android平臺下,多數(shù)情況是出現(xiàn)在圖片不當處理加載的時候愉适。
內(nèi)存管理之道嘛犯助,無非就是先理解并找出內(nèi)存泄漏的原因,再基于這些反式去合理的編碼维咸,去防范進而避免內(nèi)存開銷過大的情形剂买。學習如何合理的管理內(nèi)存,最好先 了解內(nèi)存分配的機制和原理癌蓖。只有深層次的理解了內(nèi)部的原理瞬哼,才能真正避免OOM的發(fā)生。但是本文就不介紹Jvm/Davilk內(nèi)存分配的機制了租副,如有興 趣坐慰,請查看歷史消息,以前做過題為《JVM運行時數(shù)據(jù)區(qū)域分析》的分享用僧。
Android APP的所能申請的最大內(nèi)存大小是多少结胀,有人說是16MB,有人又說是24MB责循。這種事情糟港,還是親自用自己的手機測試下比較靠譜。測試方式也比較簡 單院仿,Java中有個Runtime類秸抚,主要用作APP與運行環(huán)境交互速和,APP并不會為我們創(chuàng)建Runtime的實例,但是Java為我們提供了單例獲取的 方式Runtime.getRuntime()剥汤。通過maxMemory()方法獲取系統(tǒng)可為APP分配的最大內(nèi)存颠放,totalMemory() 獲取APP當前所分配的內(nèi)存heap空間大小。我手上有兩部手機吭敢,一部Oppo find7碰凶,運行Color OS,實測最大內(nèi)存分配為192MB省有;一部天語v9痒留,運行小米系統(tǒng),實測最大內(nèi)存分配為100MB蠢沿。這下看出點眉目了吧伸头,由于Android是開源系統(tǒng), 不同的手機廠商其實是擁有修改這部分權限能力的舷蟀,所以就造成了不同品牌和不同系統(tǒng)的手機恤磷,對于APP的內(nèi)存支持也是不一樣的,和IOS的恒久100MB是 不同的野宜。一般來說扫步,手機內(nèi)存的配置越高,廠商也會調(diào)大手機支持的內(nèi)存最大閥值匈子,尤其是現(xiàn)在旗艦機滿天發(fā)布的情況下河胎。但是開發(fā)者為了考慮開發(fā)出的APP的內(nèi) 存兼容性,無法保證APP運行在何種手機上虎敦,只能從編碼角度來優(yōu)化內(nèi)存了游岳。
下面我們逐條來分析Android內(nèi)存優(yōu)化的關鍵點。
1其徙、萬惡的static
static是個好東西胚迫,聲明賦值調(diào)用就是那么的簡單方便,但是伴隨而來的還有性能問題唾那。由于static聲明變量的生命周期其實是和APP的生命周期一 樣的访锻,有點類似與Application。如果大量的使用的話闹获,就會占據(jù)內(nèi)存空間不釋放期犬,積少成多也會造成內(nèi)存的不斷開銷,直至掛掉避诽。static的合理 使用一般用來修飾基本數(shù)據(jù)類型或者輕量級對象哭懈,盡量避免修復集合或者大對象,常用作修飾全局配置項茎用、工具類方法、內(nèi)部類。
2轨功、無關引用
很多情況下旭斥,我們需求用到傳遞引用,但是我們無法確保引用傳遞出去后能否及時的回收古涧。比如比較有代表性的Context泄漏垂券,很多情況下當Activity 結(jié)束掉后,由于仍被其他的對象指向?qū)е乱恢边t遲不能回收羡滑,這就造成了內(nèi)存泄漏菇爪。這時可以考慮第三條建議。
3柒昏、善用SoftReference/WeakReference/LruCache
Java凳宙、Android中有沒有這樣一種機制呢,當內(nèi)存吃緊或者GC掃過的情況下职祷,就能及時把一些內(nèi)存占用給釋放掉氏涩,從而分配給需要分配的地方。答案是 肯定的有梆,java為我們提供了兩個解決方案是尖。如果對內(nèi)存的開銷比較關注的APP,可以考慮使用WeakReference泥耀,當GC回收掃過這塊內(nèi)存區(qū)域時 就會回收饺汹;如果不是那么關注的話,可以使用SoftReference痰催,它會在內(nèi)存申請不足的情況下自動釋放兜辞,同樣也能解決OOM問題。同時 Android自3.0以后也推出了LruCache類陨囊,使用LRU算法就釋放內(nèi)存弦疮,一樣的能解決OOM,如果兼容3.0一下的版本蜘醋,請導入v4包胁塞。關于 第二條的無關引用的問題,我們傳參可以考慮使用WeakReference包裝一下压语。
4啸罢、謹慎handler
在處理異步操作的時候,handler + thread是個不錯的選擇胎食。但是相信在使用handler的時候扰才,大家都會遇到警告的情形,這個就是lint為開發(fā)者的提醒厕怜。handler運行于UI 線程衩匣,不斷處理來自MessageQueue的消息蕾总,如果handler還有消息需要處理但是Activity頁面已經(jīng)結(jié)束的情況下,Activity的 引用其實并不會被回收琅捏,這就造成了內(nèi)存泄漏生百。解決方案,一是在Activity的onDestroy方法中調(diào)用
handler.removeCallbacksAndMessages(null);取消所有的消息的處理柄延,包括待處理的消息蚀浆;二是聲明handler的內(nèi)部類為static。
5搜吧、Bitmap終極殺手
Bitmap的不當處理極可能造成OOM市俊,絕大多數(shù)情況都是因這個原因出現(xiàn)的。Bitamp位圖是Android中當之無愧的胖小子滤奈,所以在操作的時候當 然是十分的小心了摆昧。由于Dalivk并不會主動的去回收,需要開發(fā)者在Bitmap不被使用的時候recycle掉僵刮。使用的過程中据忘,及時釋放是非常重要 的。同時如果需求允許搞糕,也可以去BItmap進行一定的縮放勇吊,通過BitmapFactory.Options的inSampleSize屬性進行控制。 如果僅僅只想獲得Bitmap的屬性窍仰,其實并不需要根據(jù)BItmap的像素去分配內(nèi)存汉规,只需在解析讀取Bmp的時候使用 BitmapFactory.Options的inJustDecodeBounds屬性。最后建議大家在加載網(wǎng)絡圖片的時候驹吮,使用軟引用或者弱引用并進 行本地緩存针史,推薦使用android-universal-imageloader或者xUtils,牛人出品碟狞,必屬精品啄枕。前幾天在講《自定義控件(三) 繼承控件》的時候,也整理一個族沃,大家可以去Github下載看看频祝。
6、Cursor及時關閉
在查詢SQLite數(shù)據(jù)庫時脆淹,會返回一個Cursor常空,當查詢完畢后,及時關閉盖溺,這樣就可以把查詢的結(jié)果集及時給回收掉漓糙。
7、頁面背景和圖片加載
在布局和代碼中設置背景和圖片的時候烘嘱,如果是純色昆禽,盡量使用color蝗蛙;如果是規(guī)則圖形,盡量使用shape畫圖为狸;如果稍微復雜點歼郭,可以使用9patch圖;如果不能使用9patch的情況下辐棒,針對幾種主流分辨率的機型進行切圖。
8牍蜂、ListView和GridView的item緩存
對于移動設備漾根,尤其硬件參差不齊的android生態(tài),頁面的繪制其實是很耗時的鲫竞,findViewById也是蠻慢的辐怕。所以不重用View,在有列表的時候就尤為顯著了从绘,經(jīng)常會出現(xiàn)滑動很卡的現(xiàn)象寄疏。具體參照歷史文章《說說ViewHolder的另一種寫法》
9、BroadCastReceiver僵井、Service
綁定廣播和服務陕截,一定要記得在不需要的時候給解綁。
10批什、I/O流
I/O流操作完畢农曲,讀寫結(jié)束,記得關閉驻债。
11乳规、線程
線程不再需要繼續(xù)執(zhí)行的時候要記得及時關閉,開啟線程數(shù)量不易過多合呐,一般和自己機器內(nèi)核數(shù)一樣最好暮的,推薦開啟線程的時候,使用線程池淌实。
12冻辩、String/StringBuffer
當有較多的字符創(chuàng)需要拼接的時候,推薦使用StringBuffer翩伪。
今天沒有代碼微猖,純文字,純手打缘屹,蠻辛苦凛剥。整理了這么多優(yōu)化的策略,相信大家在理解后使用轻姿,再也不會遇上OOM了犁珠。