本文大體分為四部分
- 內(nèi)存優(yōu)化
- 布局優(yōu)化
- 編碼優(yōu)化
- 網(wǎng)絡優(yōu)化
內(nèi)存優(yōu)化
首先說一下內(nèi)存泄漏和OOM:
- 內(nèi)存泄漏,因為不恰當?shù)囊脤е卤驹摫会尫诺馁Y源無法得到釋放。
- OOM,新分配的內(nèi)存大小加上已經(jīng)占用的內(nèi)存大小,超出了限制的內(nèi)存大小木张。
內(nèi)存泄漏更多是因為我們的代碼寫的有問題,OOM更多是因為我們對我們應用內(nèi)存的占用沒有很好的把控。內(nèi)存泄漏是導致OOM的一大元兇刃滓。
內(nèi)存優(yōu)化分為5點來說:
- 減少對象內(nèi)存占用
- 內(nèi)存對象的復用
- 避免內(nèi)存泄漏
- 合理的內(nèi)存使用策略
- 內(nèi)存優(yōu)化輔助工具
一、減少對象內(nèi)存占用
- 使用更加輕量的數(shù)據(jù)結構耸弄。比如:ArrayMap/SparseArray是替代HashMap的好幫手咧虎。關于ArrayMap和SparseArray的使用:
- 適合對象個數(shù)的數(shù)量級最好在千以內(nèi),因為他們的插入和刪除的效率不夠高计呈。
- 查找和插入使用的是二分查找砰诵。
- key類型是int時,請使用SparseArray因為它避免了自動裝箱捌显。
- 避免使用Enum茁彭。官方說法是,相對于靜態(tài)常量扶歪,枚舉會消耗兩倍以上的內(nèi)存理肺。并且在運行時還會產(chǎn)生額外的內(nèi)存占用。在一個官方實例里善镰,枚舉占用的內(nèi)存是靜態(tài)常量的13倍妹萨,運行時內(nèi)存占用是6倍。
- 減少Bitmap對象的內(nèi)存占用炫欺。對于創(chuàng)建出來的Bitmap對象通常有兩個操作可以優(yōu)化其內(nèi)存占用乎完。
- inSampleSize:在載入內(nèi)存之前,計算一個合適的縮放比例品洛。
- decode format:解碼格式树姨,ARGB_8888(每個像素4個字節(jié),最高精度毫别,有透明通道)娃弓、RGB_565(每個像素兩個字節(jié),無透明度)岛宦、ARGB_4444(deprecated in Api13)台丛、Alpah_8,不同的解碼格式差別很大。根據(jù)情況選擇合適的會比較好挽霉。
- 使用更小的圖片防嗡。拿到美工給的圖,要留意大圖直接被XML引用有時會出現(xiàn)InflationException侠坎,該異常的根本原因就是OOM蚁趁。
二、內(nèi)存對象的復用
- 復用系統(tǒng)自帶的資源实胸。系統(tǒng)自帶了很多顏色他嫡、動畫、樣式庐完、布局钢属、圖片。使用這些可以減少內(nèi)存開銷门躯,但需注意版本差異性淆党。
- ListView/GridView/RecyclerView內(nèi)重復子View的復用。
- Bitmap對象復用讶凉。使用inBitmap來復用已經(jīng)存在的內(nèi)存區(qū)域染乌。3.0之后出現(xiàn),重用的bitmap大小和解碼格式需要一致懂讯。4.4以后優(yōu)化大小限制荷憋,只要小于或等于原bitmap的大小即可∮虼迹可以維護一個有多種典型bitmap的對象池台谊,使得后續(xù)bitmap創(chuàng)建都可以找到合適的復用模板。
- 在頻繁調(diào)用的方法外創(chuàng)建對象譬挚。如onDraw()方法會頻繁調(diào)用,在里面做創(chuàng)建對象的操作會迅速增加內(nèi)存使用酪呻,很容易引起頻繁GC甚至是內(nèi)存抖動减宣。
- StringBuilder/StringBuffer。使用StringBuilder/StringBuffer來替代頻繁的字符串拼接操作玩荠。
三漆腌、避免內(nèi)存泄漏
- Activity的泄漏。
- 內(nèi)部類引用導致阶冈。典型的如Handler闷尿。考慮盡量使用靜態(tài)內(nèi)部類女坑,同時使用弱引用機制避免互相引用出現(xiàn)的泄漏。
- Activity Context被傳遞到其他實例中,可能導致自身被引用發(fā)生泄漏臂港。盡量使用Application Context。除了和UI相關的誉简,如顯示彈窗、啟動Activity盟广、填充布局闷串。參考Android Context。
- 臨時Bitmap對象的回收筋量。臨時創(chuàng)建一個相對比較大的bitmap對象烹吵,在經(jīng)過變換獲得新的bitmap對象之后,應盡快回收之前的bitmap桨武。注意createBitmap()方法可能返回source bitmap年叮,所以需要檢查返回值是否和source bitmap相等。不等才可以對source bitmap執(zhí)行recycle方法玻募。
- 監(jiān)聽器的注銷只损。
- Cursor對象的及時關閉。
- 緩存容器的對象泄漏七咧。如4.0之前跃惫,把drawable添加到緩存容器,因為drawable和view的強引用很容易導致activity發(fā)生泄漏艾栋。
- WebView的泄露爆存。Android的WebView存在很大的兼容性問題,WebView因為不同系統(tǒng)版本不同廠商都粗乃很大的差異蝗砾,甚至標準的WebView存在內(nèi)存泄漏的問題(09年發(fā)現(xiàn)先较,13年修復)。根治方法:為WebView使用新進程悼粮,通過AIDL進行通信闲勺,WebView所在進程根據(jù)業(yè)務需要在合適時機進行銷毀。
- 慎用static對象扣猫,static的生命周期和應用的進程保持一致菜循,使用不當很可能導致內(nèi)存泄漏。
- 留意單例對象中不合理的引用申尤。單例對象的生命周期和應用保持一致癌幕。
四、合理的內(nèi)存使用策略
- 使用IntentService代替Service昧穿。
- 謹慎使用large heap勺远。在清單文件的<application>節(jié)點設置largeHeap=true可以為應用生命一個更大的heap控件。會影響用戶體驗时鸵,并使GC運行時間更長胶逢,任務切換耗能增加。并且,在一些嚴格限制的機器上宪塔,largeHeap和通常的heap size大小一樣磁奖。你始終應該通過getMemoryClass()來檢查實際獲取到的heap大小。
- 合適的緩存大小某筐。結合可用內(nèi)存大小等因素設置比搭。
- onLowMemory()和onTrimMemory()。后者從4.0開始提供南誊,提供了更為詳細的系統(tǒng)內(nèi)存占用級別身诺。可以通過監(jiān)測系統(tǒng)內(nèi)存占用適當?shù)尼尫抛陨淼囊恍﹥?nèi)存占用抄囚。
- 選擇合適的文件夾存放資源文件霉赡。圖片會被拉伸以適應不同的設備。對于不希望被拉伸的圖片幔托,放在assets或nodpi目錄下穴亏。
- 對大內(nèi)存分配做Try...Catch...操作。比如給解析大圖時重挑,使用try catch嗓化,catch到OOM后將采樣比例增加一倍再次嘗試解析。
- 慎用抽象編程谬哀。抽象需要同等量的代碼用于可執(zhí)行刺覆,這些代碼會被mapping到內(nèi)存中。
- 使用nano protobufs序列化數(shù)據(jù)史煎。Google設計谦屑,類似XML,比XML更加輕量快速簡單篇梭。
- 慎用依賴注入氢橙。通過掃描你的代碼執(zhí)行許多初始化操作,會導致你的代碼需要大量的內(nèi)存空間來mapping代碼很洋,而且mapped pages會長時間保留在內(nèi)存中充蓝。
- 慎用多進程『泶牛可以擴大應用的內(nèi)存占用范圍,但使用不當會導致顯著增加內(nèi)存官脓。
- 使用ProGuard剔除不需要的代碼协怒。
- 慎用第三方庫。很多功能會用不上卑笨。
五孕暇、內(nèi)存優(yōu)化輔助工具
- facebook開源的LeakCanary,可用來監(jiān)測內(nèi)存泄漏
- Android Monitor,可以查看內(nèi)存占用妖滔,可以查看指向隧哮,可以手動觸發(fā)GC。分析內(nèi)存泄漏的流程是
- 手動觸發(fā)GC
- 查看JavaHeap
- 點擊Analyzer Task即可進行內(nèi)存泄漏的分析座舍。
布局優(yōu)化
分四個方面
- 選擇合適的根節(jié)點
- 重用布局文件
- 僅在需要時加載布局
- 避免過度繪制
一沮翔、選擇合適的根節(jié)點
Android在創(chuàng)建Activity時默認生成的布局為RelativeLayout,而新建布局時默認的根節(jié)點為LinearLayout曲秉。這是因為
- 在復雜的布局中使用RelativeLayout可以降低布局嵌套采蚀,使布局比較扁平。也更加靈活承二。
- 對于簡單的布局榆鼠,LinearLayout在不用處理weight屬性的情況下,性能上是優(yōu)于至少需要計算兩次的RelativeLayout的亥鸠。
二妆够、重用布局文件
- <include>標簽的使用,要注意如果需要使用layout屬性负蚊,必需先設置layout:width和layout:height
- <merge>標簽的使用可減少不必要的視圖嵌套 :
- 添加的子視圖不需要針對父視圖的屬性神妹,只是要添加到父視圖上顯示。根節(jié)點可為<merge>盖桥。
- 比如在LinearLayout里include另外一個方向相同的LinearLayout灾螃,這個被include的視圖的根節(jié)點就可以改為merge.
三、僅在需要時加載布局
<ViewStub>
使用時調(diào)用inflate即可揩徊,也可以調(diào)用setVisibility(View.VISIBILITY)腰鬼。
注意:不支持<merge>標簽的布局。
四塑荒、避免過度繪制
比如給根布局設置了圖片背景熄赡,但是用戶只能看到子View,根本就沒有看到最下面的背景齿税。但是背景仍要被繪制彼硫。這就是過度繪制。
可以通過設置-開發(fā)者選項-顯示GPU過度繪制來觀察過度繪制凌箕。顏色越深的區(qū)域過度繪制越嚴重拧篮,藍色最好,紅色最差牵舱。
- 去除不必要的背景設置串绩。
- 自定義view時,通過Canvas的clipRect()來繪制部分需要重繪的區(qū)域芜壁。
五礁凡、小知識點
- android:drawableXXX屬性高氮。TextView控件可直接顯示圖片和文字。
- setCompoundDrawable()顷牌,代碼中通過該方法實現(xiàn)第一個效果剪芍。
- android:divider,使用自帶的分割線窟蓝。
- space控件罪裹,可用于添加空白間隔,該控件不進行繪制疗锐。
- android:lineSpacingExtra="",android:text="aaa\nbbb\nccc"多行文字可使用TextView的行間距實現(xiàn)坊谁。
- Spannable,使用Spannable來為TextView設置強大的樣式滑臊。
編碼優(yōu)化
幾個點
- 靜態(tài)方法口芍。將一項通用的功能寫成靜態(tài)方法,調(diào)用速度會提升15%-20%雇卷,同時不需要創(chuàng)建對象來調(diào)用該方法鬓椭,也不用擔心改變對象的狀態(tài)(靜態(tài)方法無法訪問非靜態(tài)字段)。
- 避免創(chuàng)建不必要的對象
- 靜態(tài)最終常量关划。對基本數(shù)據(jù)類型以及String常量使用static final修飾小染,會在dex文件的初始化器中進行初始化,會更快贮折。
- 多使用增強型for循環(huán)裤翩,但ArrayList使用傳統(tǒng)循環(huán)方式。
- 使用系統(tǒng)封裝好的API调榄。系統(tǒng)的API很多功能是通過底層匯編模式執(zhí)行的踊赠,效率會比較高。如數(shù)組拷貝的功能每庆,使用System.arrayCopy()會比使用循環(huán)一一賦值效率高9倍以上筐带。
- 避免在內(nèi)部使用getter/setter。內(nèi)部使用時缤灵,字段查找比方法調(diào)用效率高伦籍。
網(wǎng)絡優(yōu)化
工具:Android Studio有Network Monitor
主要有:
- 接口設置多樣化,便于App可以以較少的請求來完成業(yè)務需求腮出。
- 使用Gzip壓縮request和response帖鸦,減少數(shù)據(jù)傳輸大小
- 可以使用Protocol Buffer來代替json、XML等
- 合適的圖片胚嘲。獲取圖片時告知服務器寬高質(zhì)量等來獲取合適的圖片資源富蓄。
- 設置網(wǎng)絡緩存,來取消不必要的網(wǎng)絡請求慢逾。
- 在網(wǎng)絡良好的時候立倍,對一些很有可能會進行操作的數(shù)據(jù)進行提前獲取。
- 弱網(wǎng)優(yōu)化侣滩,Android Emulator可以設置網(wǎng)絡速度和延遲來做弱網(wǎng)測試口注。可采取的措施如:不自動加載圖片君珠、先反饋后提交(如用戶點贊寝志,先給提交成功的反饋,記錄下來之后提交)