Android性能優(yōu)化總結(jié)

本文大體分為四部分
  1. 內(nèi)存優(yōu)化
  2. 布局優(yōu)化
  3. 編碼優(yōu)化
  4. 網(wǎng)絡(luò)優(yōu)化

內(nèi)存優(yōu)化

主要參考胡凱文章

首先說一下內(nèi)存泄漏和OOM:
  1. 內(nèi)存泄漏,因為不恰當(dāng)?shù)囊脤?dǎo)致本該被釋放的資源無法得到釋放。
  2. OOM,新分配的內(nèi)存大小加上已經(jīng)占用的內(nèi)存大小,超出了限制的內(nèi)存大小。

內(nèi)存泄漏更多是因為我們的代碼寫的有問題,OOM更多是因為我們對我們應(yīng)用內(nèi)存的占用沒有很好的把控。內(nèi)存泄漏是導(dǎo)致OOM的一大元兇。

內(nèi)存優(yōu)化分為5點來說:
  1. 減少對象內(nèi)存占用
  2. 內(nèi)存對象的復(fù)用
  3. 避免內(nèi)存泄漏
  4. 合理的內(nèi)存使用策略
  5. 內(nèi)存優(yōu)化輔助工具

一、減少對象內(nèi)存占用

  1. 使用更加輕量的數(shù)據(jù)結(jié)構(gòu)。比如:ArrayMap/SparseArray是替代HashMap的好幫手。關(guān)于ArrayMap和SparseArray的使用:
  • 適合對象個數(shù)的數(shù)量級最好在千以內(nèi),因為他們的插入和刪除的效率不夠高。
  • 查找和插入使用的是二分查找。
  • key類型是int時,請使用SparseArray因為它避免了自動裝箱。
  1. 避免使用Enum。官方說法是,相對于靜態(tài)常量,枚舉會消耗兩倍以上的內(nèi)存。并且在運行時還會產(chǎn)生額外的內(nèi)存占用。在一個官方實例里,枚舉占用的內(nèi)存是靜態(tài)常量的13倍,運行時內(nèi)存占用是6倍。
  2. 減少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ù)情況選擇合適的會比較好。
  1. 使用更小的圖片。拿到美工給的圖,要留意大圖直接被XML引用有時會出現(xiàn)InflationException,該異常的根本原因就是OOM。

二、內(nèi)存對象的復(fù)用

  1. 復(fù)用系統(tǒng)自帶的資源。系統(tǒng)自帶了很多顏色、動畫、樣式、布局、圖片。使用這些可以減少內(nèi)存開銷,但需注意版本差異性。
  2. ListView/GridView/RecyclerView內(nèi)重復(fù)子View的復(fù)用。
  3. Bitmap對象復(fù)用。使用inBitmap來復(fù)用已經(jīng)存在的內(nèi)存區(qū)域。3.0之后出現(xiàn),重用的bitmap大小和解碼格式需要一致。4.4以后優(yōu)化大小限制,只要小于或等于原bitmap的大小即可??梢跃S護一個有多種典型bitmap的對象池,使得后續(xù)bitmap創(chuàng)建都可以找到合適的復(fù)用模板。
  4. 在頻繁調(diào)用的方法外創(chuàng)建對象。如onDraw()方法會頻繁調(diào)用,在里面做創(chuàng)建對象的操作會迅速增加內(nèi)存使用,很容易引起頻繁GC甚至是內(nèi)存抖動。
  5. StringBuilder/StringBuffer。使用StringBuilder/StringBuffer來替代頻繁的字符串拼接操作。

三、避免內(nèi)存泄漏

  1. Activity的泄漏。
  • 內(nèi)部類引用導(dǎo)致。典型的如Handler??紤]盡量使用靜態(tài)內(nèi)部類,同時使用弱引用機制避免互相引用出現(xiàn)的泄漏。
  • Activity Context被傳遞到其他實例中,可能導(dǎo)致自身被引用發(fā)生泄漏。盡量使用Application Context。除了和UI相關(guān)的,如顯示彈窗、啟動Activity、填充布局。參考Android Context。
  1. 臨時Bitmap對象的回收。臨時創(chuàng)建一個相對比較大的bitmap對象,在經(jīng)過變換獲得新的bitmap對象之后,應(yīng)盡快回收之前的bitmap。注意createBitmap()方法可能返回source bitmap,所以需要檢查返回值是否和source bitmap相等。不等才可以對source bitmap執(zhí)行recycle方法。
  2. 監(jiān)聽器的注銷。
  3. Cursor對象的及時關(guān)閉。
  4. 緩存容器的對象泄漏。如4.0之前,把drawable添加到緩存容器,因為drawable和view的強引用很容易導(dǎo)致activity發(fā)生泄漏。
  5. WebView的泄露。Android的WebView存在很大的兼容性問題,WebView因為不同系統(tǒng)版本不同廠商都粗乃很大的差異,甚至標(biāo)準(zhǔn)的WebView存在內(nèi)存泄漏的問題(09年發(fā)現(xiàn),13年修復(fù))。根治方法:為WebView使用新進程,通過AIDL進行通信,WebView所在進程根據(jù)業(yè)務(wù)需要在合適時機進行銷毀。
  6. 慎用static對象,static的生命周期和應(yīng)用的進程保持一致,使用不當(dāng)很可能導(dǎo)致內(nèi)存泄漏。
  7. 留意單例對象中不合理的引用。單例對象的生命周期和應(yīng)用保持一致。

四、合理的內(nèi)存使用策略

  1. 使用IntentService代替Service。
  2. 謹(jǐn)慎使用large heap。在清單文件的<application>節(jié)點設(shè)置largeHeap=true可以為應(yīng)用生命一個更大的heap控件。會影響用戶體驗,并使GC運行時間更長,任務(wù)切換耗能增加。并且,在一些嚴(yán)格限制的機器上,largeHeap和通常的heap size大小一樣。你始終應(yīng)該通過getMemoryClass()來檢查實際獲取到的heap大小。
  3. 合適的緩存大小。結(jié)合可用內(nèi)存大小等因素設(shè)置。
  4. onLowMemory()和onTrimMemory()。后者從4.0開始提供,提供了更為詳細的系統(tǒng)內(nèi)存占用級別??梢酝ㄟ^監(jiān)測系統(tǒng)內(nèi)存占用適當(dāng)?shù)尼尫抛陨淼囊恍﹥?nèi)存占用。
  5. 選擇合適的文件夾存放資源文件。圖片會被拉伸以適應(yīng)不同的設(shè)備。對于不希望被拉伸的圖片,放在assets或nodpi目錄下。
  6. 對大內(nèi)存分配做Try...Catch...操作。比如給解析大圖時,使用try catch,catch到OOM后將采樣比例增加一倍再次嘗試解析。
  7. 慎用抽象編程。抽象需要同等量的代碼用于可執(zhí)行,這些代碼會被mapping到內(nèi)存中。
  8. 使用nano protobufs序列化數(shù)據(jù)。Google設(shè)計,類似XML,比XML更加輕量快速簡單。
  9. 慎用依賴注入。通過掃描你的代碼執(zhí)行許多初始化操作,會導(dǎo)致你的代碼需要大量的內(nèi)存空間來mapping代碼,而且mapped pages會長時間保留在內(nèi)存中。
  10. 慎用多進程??梢詳U大應(yīng)用的內(nèi)存占用范圍,但使用不當(dāng)會導(dǎo)致顯著增加內(nèi)存。
  11. 使用ProGuard剔除不需要的代碼。
  12. 慎用第三方庫。很多功能會用不上。

五、內(nèi)存優(yōu)化輔助工具

  1. facebook開源的LeakCanary,可用來監(jiān)測內(nèi)存泄漏
  2. Android Monitor,可以查看內(nèi)存占用,可以查看指向,可以手動觸發(fā)GC。分析內(nèi)存泄漏的流程是
  3. 手動觸發(fā)GC
  4. 查看JavaHeap
  5. 點擊Analyzer Task即可進行內(nèi)存泄漏的分析。

布局優(yōu)化

分四個方面

  1. 選擇合適的根節(jié)點
  2. 重用布局文件
  3. 僅在需要時加載布局
  4. 避免過度繪制

一、選擇合適的根節(jié)點

Android在創(chuàng)建Activity時默認(rèn)生成的布局為RelativeLayout,而新建布局時默認(rèn)的根節(jié)點為LinearLayout。這是因為

  1. 在復(fù)雜的布局中使用RelativeLayout可以降低布局嵌套,使布局比較扁平。也更加靈活。
  2. 對于簡單的布局,LinearLayout在不用處理weight屬性的情況下,性能上是優(yōu)于至少需要計算兩次的RelativeLayout的。

二、重用布局文件

  1. <include>標(biāo)簽的使用,要注意如果需要使用layout屬性,必需先設(shè)置layout:width和layout:height
  2. <merge>標(biāo)簽的使用可減少不必要的視圖嵌套 :
  3. 添加的子視圖不需要針對父視圖的屬性,只是要添加到父視圖上顯示。根節(jié)點可為<merge>。
  4. 比如在LinearLayout里include另外一個方向相同的LinearLayout,這個被include的視圖的根節(jié)點就可以改為merge.

三、僅在需要時加載布局

<ViewStub>
使用時調(diào)用inflate即可,也可以調(diào)用setVisibility(View.VISIBILITY)。
注意:不支持<merge>標(biāo)簽的布局。

四、避免過度繪制

比如給根布局設(shè)置了圖片背景,但是用戶只能看到子View,根本就沒有看到最下面的背景。但是背景仍要被繪制。這就是過度繪制。
可以通過設(shè)置-開發(fā)者選項-顯示GPU過度繪制來觀察過度繪制。顏色越深的區(qū)域過度繪制越嚴(yán)重,藍色最好,紅色最差。

  1. 去除不必要的背景設(shè)置。
  2. 自定義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設(shè)置強大的樣式。

編碼優(yōu)化

幾個點

  1. 靜態(tài)方法。將一項通用的功能寫成靜態(tài)方法,調(diào)用速度會提升15%-20%,同時不需要創(chuàng)建對象來調(diào)用該方法,也不用擔(dān)心改變對象的狀態(tài)(靜態(tài)方法無法訪問非靜態(tài)字段)。
  2. 避免創(chuàng)建不必要的對象
  3. 靜態(tài)最終常量。對基本數(shù)據(jù)類型以及String常量使用static final修飾,會在dex文件的初始化器中進行初始化,會更快。
  4. 多使用增強型for循環(huán),但ArrayList使用傳統(tǒng)循環(huán)方式。
  5. 使用系統(tǒng)封裝好的API。系統(tǒng)的API很多功能是通過底層匯編模式執(zhí)行的,效率會比較高。如數(shù)組拷貝的功能,使用System.arrayCopy()會比使用循環(huán)一一賦值效率高9倍以上。
  6. 避免在內(nèi)部使用getter/setter。內(nèi)部使用時,字段查找比方法調(diào)用效率高。

網(wǎng)絡(luò)優(yōu)化

工具:Android Studio有Network Monitor

主要有:

  1. 接口設(shè)置多樣化,便于App可以以較少的請求來完成業(yè)務(wù)需求。
  2. 使用Gzip壓縮request和response,減少數(shù)據(jù)傳輸大小
  3. 可以使用Protocol Buffer來代替json、XML等
  4. 合適的圖片。獲取圖片時告知服務(wù)器寬高質(zhì)量等來獲取合適的圖片資源。
  5. 設(shè)置網(wǎng)絡(luò)緩存,來取消不必要的網(wǎng)絡(luò)請求。
  6. 在網(wǎng)絡(luò)良好的時候,對一些很有可能會進行操作的數(shù)據(jù)進行提前獲取。
  7. 弱網(wǎng)優(yōu)化,Android Emulator可以設(shè)置網(wǎng)絡(luò)速度和延遲來做弱網(wǎng)測試??刹扇〉拇胧┤纾翰蛔詣蛹虞d圖片、先反饋后提交(如用戶點贊,先給提交成功的反饋,記錄下來之后提交)
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容