從本篇開始一直到 “ 如何避免 ANR”, 我們介紹 AndroidDeveloper Practices for Performance,我儘量用最精簡易懂的方式將這些精要原則呈現給大家,詳細的說明還請參考原文檔:
http://developer.android.com/training/best-performance.html
先講個故事輕鬆一下:
公元15世紀英國的國王查理三世與自己的反叛大臣亨利在波斯沃斯進行了一場大決戰,由於查理三世的戰馬少了一顆馬蹄釘,結果衝殺時戰馬受驚,將查理三世摔倒在地,士兵們見狀四顧逃命,戰爭局勢瞬間逆轉,可憐的查理被俘後大呼:“我的國家就毀滅在一顆馬蹄釘上面!”
本章谷歌給我們提出了一些在 android 開發中提升代碼性能的很多細節注意點,每一條看起來都可有可無,可它們都是提升代碼全局性能的馬蹄釘。
1 不要創建多餘對象
創建對象總會佔用內存和CPU資源,應儘量避免創建不必要的對象,好比說:
- 如果一個方法返回的 String 類型值總是連接到一個 StringBuffer 上,應修改其返回類型和實現,避免創建多餘的臨時對象;
- 若總是只需要一個 sub String,則不要返回整個 String;
- 一個 int 一維數組優於一個 Integer 數組,兩個等長的一維數組優於一個 (int, int) 對象的一維數組,同樣的情況適應於所有基本數據類型;
- 一個 Foo[] 和一個 Bar[] 要優於一個單獨的 (Foo, Bar)[]。
2 擅用靜態方法
如果你的方法無需訪問對象中的任何域,請將其置爲靜態方法,其調用速度比一般方法快15% - 20%,另外,這也是個好的習慣,如果你能保持這樣做,那麼以後你看到這樣的方法就能知道它不會更改對象的狀態了。
3 使用 static final 修飾常量
對於基本數據類型和 String 類型的常量,用 static final 來修飾比只用 static 修飾的效率要高,其它複雜數據類型的常量無此提升,但請儘量用 staticfinal 來修飾任何常量。
4 避免內部調用 Getters/Setters
在面嚮對象語言中使用 Getters/Setters 方法來獲取和設置域的值是一個良好的習慣,但在 Android 中卻很低效,除非在外部當中調用,你應該總是避免在類的內部使用 Getters/Setters,而應該直接訪問這個域。
在沒有 JIT 的情況下,直接訪問域比Getters/Setters 快上3倍,而在有 JIT 時則快上7倍。
5 使用增強的 for 循環語法
請看三種 for 循環:
1. for (int i = 0; i < mArray.length; ++i)
2. int len = localArray.length; for (int i = 0; i < len; ++i)
3. for (Foo a : mArray)
for 循環速度從 1 到 3 依次加快。第 3 種爲增強型 for 循環,除了 ArrayList 以外,我們默認都應該使用增強型 for 循環來遍歷你的集合。
6 避免讓私有內部類直接訪問外部類的私有成員
每當內部類訪問外部類的私有成員時,編譯器會爲每一個外部私有成員額外創建一個靜態訪問方法,爲了避免這種低效訪問,應該將需要被內部類訪問的外部私有成員訪問權限修改爲包權限(package-access,默認、公有、保護權限都爲此權限),但應該注意到,這些成員同時暴露給了包內的所有其它類。
7 少用浮點數
根據經驗,在 android 設備中,浮點運算的速度比整型運算慢了2倍。
8 儘量用庫方法
好比說,在 JIT 編譯環境下,System.arraycopy() 比純手動數組拷貝要快上9倍。
9 慎用 Native 方法
谷歌的工程師認爲,native 方法並不一定比 Java 方法更高效,而且還會帶來很多不必要的麻煩,除非有現成的 Native 代碼庫,且並非是爲了提升速度,否則請慎用 Native 方法。
11 不要迷信性能教條
這些性能小訣竅不是萬能的,有時候需要更好的代碼設計和可維護性可以犧牲部分性能,比如通常情況下,直接用 HashMap 的方法比其接口 Map 通過多態調用方法效率要高,可是我們知道接口的設計能給我們帶來代碼維護上的方便,而且引入 JIT 之後,這樣的效率差可以忽略不計。
總之,原則可以儘量遵守,但教條不能迷信。
12 多測試你的性能
請參考Caliper 和 Traceview 性能測試工具。