本文譯自Android開發(fā)者網(wǎng)站,主要介紹了提升Android應用性能表現(xiàn)的幾個建議骑丸。閱讀本文時還要務必記得“過早優(yōu)化是萬惡之源”,優(yōu)化起碼應該放在實現(xiàn)了應用的MVP版本之后绪抛。
原文地址:https://developer.android.com/training/articles/perf-tips.html
本文主要介紹了提升Android應用性能的一些小方法崭放,組合使用這些方法往往能夠改善我們所開發(fā)的應用的性能表現(xiàn)。但是扣甲,我們應優(yōu)先關注應用所選用的數(shù)據(jù)結構和算法篮赢,切勿本末倒置。想寫出高效代碼琉挖,有兩個基本原則:
- 不要做本不必做的工作启泣;
- 不要分配不必要的內存。
以上兩個基本原則很好理解示辈,第一條是讓我們盡可能降低應用的時間復雜度种远,第二條是讓我們盡可能降低應用的空間復雜度。下文的各個建議實際上也是圍繞著這兩個基本原則展開的顽耳。
當我們對Android應用做優(yōu)化時,我們必須面對的一個最蛋疼的問題之一妙同,便是我們的應用將會在各種類型的設備上運行射富,這意味著它們往往有著不同的硬件架構。不同版本的Android虛擬機在各種硬件架構上會以不同的速率運行粥帚。而我們大多數(shù)情況下無法簡單地斷定胰耗,X設備的速度會是Y設備的多少倍。我們在模擬器上的測試結果芒涡,對我們做真機上的性能評估又往往沒什么幫助柴灯。
更令人蛋疼的是卖漫,支持即時編譯(JIT)的設備和不支持即時編譯的設備又有著巨大的差異——支持即時編譯的設備上的最優(yōu)代碼,對于不支持即時編譯的設備來說赠群,不總是最優(yōu)的羊始。
要確保你的應用在各種各樣的設備上都有著良好的性能表現(xiàn),要確保你的代碼在各種情況下都是高效的查描,這需要我們下一番功夫來對代碼進行優(yōu)化突委。以下是一些性能優(yōu)化的建議。
避免創(chuàng)建不必要的對象
創(chuàng)建對象總是會產生代價的冬三。一個支持線程級分配池的分代垃圾回收器可以使得一次內存分配所花的代價更少匀油,但是再少也少不過“根本不進行內存分配”。
當你的應用中創(chuàng)建了足夠多的對象勾笆,便會導致垃圾回收經(jīng)常發(fā)生敌蚜,這可能會帶來用戶界面的“卡頓”。因此窝爪,你應該避免創(chuàng)建不必要的對象弛车,比如:若你有一個返回一個String的方法,并且你確信返回結果總是會被添加到一個StringBuffer(StringBuilder)中酸舍,那么應對該方法做出修改帅韧,讓它不再返回String,而是直接把結果添加到相應的StringBuffer(StringBuilder)中啃勉,這樣一來便可以避免創(chuàng)建一個臨時的String對象忽舟。
還有一個更“激進”的建議是把多維數(shù)組都“展開”成一維數(shù)組:
- 多個int數(shù)組要比一個int[]對象數(shù)組更加高效,這對于其他原始數(shù)據(jù)類型(primitive data type)同樣適用淮阐;
- 若你需要一個存儲(Foo, Bar)元組的容器叮阅,要記得使用Foo數(shù)組和Bar數(shù)組要比使用(Foo, Bar)對象數(shù)組高效的多(例外情況是當你設計API時,為了一個良好的API設計泣特,我們應該在性能上做出小小的妥協(xié))浩姥;
對于本條建議,概括起來就是盡可能避免創(chuàng)建短時存活的對象状您。
盡量使用static而不是virtual
若你不需要訪問對象的字段勒叠,那么請定義你的方法為靜態(tài)(static)方法而不是虛(virtual)方法,這會帶來15%到20%的性能提升膏孟。這同樣也是一個好的編程實踐眯分,因為如此一來我們可以清楚的知道,調用該方法不會改變對象的狀態(tài)柒桑。
對于常量使用static final
考慮下面的聲明:
static int intVal = 42;
static String strVal = "Hello, world!";
當聲明了以上語句的類被初次使用時弊决,編譯器會為之創(chuàng)建一個名為“<clinit>“的類初始化器方法。這個方法會將42存儲在intVal中魁淳,并將字符串“Hello, world!”的引用存儲在strVal中飘诗,稍后我們引用到這兩個變量時与倡,便會通過“字段查找(field lookup)”來訪問它們。
然而通過為以上兩句變量聲明加上“final”關鍵字昆稿,那么intVal和strVal就會變?yōu)閮蓚€常量纺座,訪問它們時便無需通過字段查找,這樣會提升性能貌嫡。
注意:這個優(yōu)化建議只對String和原始數(shù)據(jù)類型有效比驻。
使用增強版循環(huán)語法
增強版循環(huán)語法指的就是for-each寫法,它可以用于數(shù)組以及實現(xiàn)了Iterable接口的集合類岛抄。for-each只有對ArrayList使用時别惦,要比常規(guī)的for循環(huán)慢,對于其他情況夫椭,for-each與常規(guī)for速度相差無幾掸掸。由于for-each能夠簡化代碼編寫,我們優(yōu)先考慮使用它蹭秋;只有我們使用ArrayList并且追求“極致性能”時扰付,才應使用常規(guī)for循環(huán)。
對以下場景考慮使用包(package)而不是私有(private)
考慮下面的代碼:
public class Foo {
private class Inner {
void stuff() {
Foo.this.doStuff(Foo.this.mValue);
}
}
private int mValue;
public void run() {
Inner in = new Inner();
mValue = 27;
in.stuff();
}
private void doStuff(int value) {
System.out.println("Value is " + value);
}
}
以上代碼的問題在于仁讨,我們在Foo類的私有內部類Inner中訪問了Foo類的私有方法和私有字段羽莺,盡管這在Java語法中是合法的。但是對虛擬機來說洞豁,會將Foo$Inner和Foo視為兩個不同的類盐固,所以虛擬機為了實現(xiàn)內部類對外圍類私有方法/字段的訪問,需要創(chuàng)建兩個充當“溝通紐帶”的方法丈挟,如下:
static int Foo.access$100(Foo foo) {
return foo.mValue;
}
static void Foo.access$200(Foo foo, int value) {
foo.doStuff(value);
}
也就是說刁卜,內部類Inner要通過上面兩個方法來訪問外圍類的私有字段/方法,這顯然比直接字段訪問的開銷要大曙咽。這種情況下蛔趴,我們可以考慮將mValue和doStuff()的可見性改為默認的包范圍。當然例朱,我們不應該對公共API應用這一點孝情。
避免使用浮點型
對于Android設備,使用浮點數(shù)要比整數(shù)大概慢兩倍洒嗤;而雙精度浮點和單精度浮點在時間效率上相差無幾咧叭,只是前者會占用二倍于后者的存儲空間。還應該注意的是烁竭,有些Android設備在硬件層面上不支持除法。我們在開發(fā)中也要注意這一點吉挣。
學習并使用系統(tǒng)API
有時候對于某種業(yè)務邏輯派撕,與自己實現(xiàn)相比婉弹,我們更應該優(yōu)先使用SDK提供給我們的方法,因為系統(tǒng)提供給我們的實現(xiàn)往往更加高效终吼。一個典型的例子是System.arrayCopy()方法要比我們手動用循環(huán)進行數(shù)組復制快9倍左右(在支持即時編譯的Nexus One設備上)镀赌。
可能無需進行的優(yōu)化
我們先來考慮以下兩個方法:
void doWork(Map map);
void doWork(HashMap map);
在一個不支持即時編譯的設備上,第一個方法只比第二個方法慢一點(6%)际跪;而支持即時編譯的設備上商佛,二者效率的差異就更小了。
我們再來考慮一下”重復訪問字段”和”把字段緩存為局部變量”所帶來的性能差異姆打。在不支持即時編譯的設備上良姆,緩存要比重復訪問快20%,而對于支持即時編譯的設備幔戏,兩者幾乎一樣快玛追。
因此對于以上兩種情況,無需我們費心進行“優(yōu)化”闲延。
記得做性能測試
在你著手進行優(yōu)化之前痊剖,確保你已經(jīng)發(fā)現(xiàn)了性能問題,畢竟“過早優(yōu)化是萬惡之源”垒玲。此外陆馁,還要確保你已經(jīng)精確測試過應用現(xiàn)階段的性能表現(xiàn),否則我們難以衡量優(yōu)化工作的成效合愈。我們可以使用SysTrace和TraceView來量化我們應用的性能表現(xiàn)叮贩。
長按或掃描二維碼關注我們,讓您利用每天等地鐵的時間就能學會怎樣寫出優(yōu)質app想暗。