|
Android應用程序運行的移動設備受限于其運算能力,存儲空間,及電池續航。由此,它必須是高效的。電池續航可能是一個促使你優化程序的原因,即使他看起來已經運行的足夠快了。由于續航對用戶的重要性,當電量耗損陡增時,意味這用戶遲早會發現是由于你的程序。
雖然這份文檔主要包含著細微的優化,但這些絕不能成為你軟件成敗的關鍵。選擇合適的算法和數據結構永遠是你最先應該考慮的事情,但這超出這份文檔之外。
1. 介紹
寫出高效的代碼有兩條基本的原則:
◆ 不作沒有必要的工作
◆ 盡量避免內存分配。
2. 明智的優化
這份文檔是關于Android規范的細微優化,所以先確保你已經了解哪些代碼需要優化,并且知道如何去衡量你所做修改所帶來的效果(好或壞)。用開投資開發的時間是有限的,所以明智的時間規劃很重要。
這份文檔同時確保你在算法和數據結構上作出最佳選擇,同時考慮了API選擇所帶來的潛在影響。使用恰當的數據結構和算法比這里的任何建議都有價值,考慮API版本帶來的影響會如實你選擇更好的實現。
當你優化Android程序時會遇到的一個棘手問題是確保你的程序能在不同的硬件平臺上運行。不同版本的虛擬機在不同處理器上的運行速度各不相同。并且不是簡單的設備A比設備B快或者慢,并針對一個設備與其他設備之間做出排列。特別的,模擬器上只能評測小部分可以在設備上體現的東西。有無JIT的設備間也有著巨大差異:對于有JIT設備好的代碼有時對無JIT的設備并不是最好的。
如果你想知道程序在設備上的表現,就必須在上面進行測試
3. 避免創建不必要的對象
對象創建永遠不會免費的。每個線程的分代GC給臨時對象分配一個地址池能降低分配開銷,但分配內存往往需要比不分配內存高的代價。
如果在用戶界面周期內分配對象,會強制一個周期性的垃圾回收,給用戶體驗造成小小的停頓間隙。Gingerbread中介紹的并發回收也許有用,但應該避免不必要的工作。
因此,避免創建不需要的對象實例。下面是幾個例子:
◆ 如果有一個返回String的方法,他的返回值通常附加在一個StringBuffer上,改變聲明和實現,這樣函數直接在其后面附加,而非創建一個短暫存在的臨時變量。
◆ 當從輸入的數據集合中讀取數據時,考慮返回原始數據的子串,而非新建一個拷貝。這樣你會創建一個新的對象,但是他們共享該數據的char數組。換來的是即使你僅僅使用原始輸入的一部分,你也需要保證它一直存在于內存中。
一個更徹底的觀點是將多維數組切割成一維數組:
◆ Int類型的數組比Integer類型的好。推而廣之,兩個平行的int數組要比一個(int,int)型的對象數組高效。這個定理對于任何基本數據類型的組合都通用。
◆ 如果需要實現存放元組(Foo,Bar)對象的容器,記住兩個平行數組Foo[], Bar[]會優于一個(Foo,Bar)對象的數組。(例外情況是:當你設計API給其他代碼調用時,最好用好的API設計來換取小的速度提升。但在自己的內部代碼中,盡量嘗試高效的實現。)
通常來說,盡量避免創建短時臨時對象。少的對象創建意味著低頻的垃圾回收。這對于用戶體驗產生直接的影響。
4. 性能之謎
前一個版本的文檔給出了好多誤導人的主張,這里做一些澄清:
◆ 在沒有JIT的設備上,調用方法所傳遞的對象采用具體的類型而非接口類型會更有效(比如,傳遞HashMap map比傳遞Map map調用一個方法耗費的開銷小,盡管兩種情況下的map都是HashMap)。但這并不是兩倍慢的情形,事實上,只相差6%,而JIT使這兩種調用的效率不分伯仲。
◆ 在沒有JIT的設備上,訪問緩存后的字段比直接訪問字段快大概20%。在有JIT的情況下,字段訪問和局部訪問耗費是一樣的 。所以這里不值得優化,除非你覺得他會讓你的代碼更易讀(對于final,static,及static final 變量同樣適用).
5. 用靜態代替虛擬
如果不需要訪問某對象的字段,將方法設置為靜態,調用會加速15%到20%。這也是一種好的做法,因為你可以通過方法聲明知曉調用該方法不需要更新此對象的狀態。
6. 避免內部的Getters/Setters
在源生語言像C++中,通常做法是用Getters(i=getCount())代替直接訪問字段(i=mCount)。這是C++中一個好的習慣,因為編譯器會內聯這些訪問,如果需要約束或者調試這些域的訪問,你可以在任何時間添加代碼。
在Android中,這是個不好的想法。虛方法調用代價比直接存取字段高昂的多。按照通常面向對象語言的做法在公共接口中使用Getters和Setters是有原因的,但應該在一個經常訪問其字段的類中采用直接訪問。
無JIT時,直接字段訪問大約比調用無關緊要的getter來訪問快3倍。有JIT時(直接訪問字段開銷和訪問局部變量是一樣的),要快7倍。在Froyo版本中確實如此,但以后會在JIT中改進Getter方法的內聯。
7. 對常量使用Static Final修飾符
考慮下面類首的聲明:
Java代碼
static int intVal = 42;
static String strVal = "Hello, world!";
it知識庫:官方文檔:Android應用程序運行的性能設計,轉載需保留來源!
鄭重聲明:本文版權歸原作者所有,轉載文章僅為傳播更多信息之目的,如作者信息標記有誤,請第一時間聯系我們修改或刪除,多謝。