編輯:Android資訊
這篇文章主要是介紹了一些小細節的優化技巧,當這些小技巧綜合使用起來的時候,對於整個Android App的性能提升還是有作用的,只是不能較大幅度的提升性能而已。選擇合適的算法與數據結構才應該是你首要考慮的因素,在這篇文章中不會涉及這方面。你應該使用這篇文章中的小技巧作為平時寫代碼的習慣,這樣能夠提升代碼的效率。
原文: http://developer.android.com/training/articles/perf-tips.html
原翻譯地址:http://hukai.me/android-training-course-in-chinese/performance/performance-tips.html
通常來說,高效的代碼需要滿足下面兩個規則:
代碼的執行效果會受到設備CPU,設備內存,系統版本等諸多因素的影響。為了確保代碼能夠在不同設備上都運行良好,需要最大化代碼的效率。
雖然GC可以回收不用的對象,可是為這些對象分配內存,並回收它們同樣是需要耗費資源的。 因此請盡量避免創建不必要的對象,有下面一些例子來說明這個問題:
如果你不需要訪問一個對象的值域,請保證這個方法是static類型的,這樣方法調用將快15%-20%。這是一個好的習慣,因為你可以從方法聲明中得知調用無法改變這個對象的狀態。
先看下面這種聲明的方式
static int intVal = 42; static String strVal = "Hello, world!";
編譯器會在類首次被使用到的時候,使用初始化方法來初始化上面的值,之後訪問的時候會需要先到它那裡查找,然後才返回數據。我們可以使用static final來提升性能:
static final int intVal = 42; static final String strVal = "Hello, world!";
這時再也不需要上面的那個方法來做多余的查找動作了。 所以,請盡可能的為常量聲明為static final類型的。
像C++等native language,通常使用getters(i = getCount())而不是直接訪問變量(i = mCount).這是編寫C++的一種優秀習慣,而且通常也被其他面向對象的語言所采用,例如C#與Java,因為編譯器通常會做inline訪問,而且你需要限制或者調試變量,你可以在任何時候在getter/setter裡面添加代碼。 然而,在Android上,這是一個糟糕的寫法。Virtual method的調用比起直接訪問變量要耗費更多。那麼合理的做法是:在面向對象的設計當中應該使用getter/setter,但是在類的內部你應該直接訪問變量。 沒有JIT(Just In Time Compiler)時,直接訪問變量的速度是調用getter的3倍。有JIT時,直接訪問變量的速度是通過getter訪問的7倍。 請注意,如果你使用ProGuard, 你可以獲得同樣的效果,因為ProGuard可以為你inline accessors.
請比較下面三種循環的方法:
static class Foo { int mSplat; } Foo[] mArray = ... public void zero() { int sum = 0; for (int i = 0; i < mArray.length; ++i) { sum += mArray[i].mSplat; } } public void one() { int sum = 0; Foo[] localArray = mArray; int len = localArray.length; for (int i = 0; i < len; ++i) { sum += localArray[i].mSplat; } } public void two() { int sum = 0; for (Foo a : mArray) { sum += a.mSplat; } }
zero()是最慢的,因為JIT沒有辦法對它進行優化。
one()稍微快些。
two() 在沒有做JIT時是最快的,可是如果經過JIT之後,與方法one()是差不多一樣快的。它使用了增強的循環方法for-each。
所以請盡量使用for-each的方法,但是對於ArrayList,請使用方法one()。
參考下面一段代碼
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),它直接訪問了外部類中的私有方法以及私有成員對象。這是合法的,這段代碼也會如同預期一樣打印出”Value is 27”。
問題是,VM因為Foo和(Foo.Inner)是不同的類,會認為在 (Foo.Inner)中直接訪問Foo類的私有成員是不合法的。即使Java語言允許內部類訪問外部類的私有成員。為了去除這種差異,編譯器會產生一些仿造函數:
/*package*/ static int Foo.access$100(Foo foo) { return foo.mValue; } /*package*/ static void Foo.access$200(Foo foo, int value) { foo.doStuff(value); }
每當內部類需要訪問外部類中的mValue成員或需要調用doStuff()函數時,它都會調用這些靜態方法。這意味著,上面的代碼可以歸結為,通過accessor函數來訪問成員變量。早些時候我們說過,通過accessor會比直接訪問域要慢。所以,這是一個特定語言用法造成性能降低的例子。
如果你正在性能熱區(hotspot:高頻率、重復執行的代碼段)使用像這樣的代碼,你可以把內部類需要訪問的域和方法聲明為包級訪問,而不是私有訪問權限。不幸的是,這意味著在相同包中的其他類也可以直接訪問這些域,所以在公開的API中你不能這樣做。
Android系統中float類型的數據存取速度是int類型的一半,盡量優先采用int類型。
盡量使用System.arraycopy()等一些封裝好的庫函數,它的效率是手動編寫copy實現的9倍多。
Tip: Also see Josh Bloch’s Effective Java, item 47.
當你需要把已經存在的native code遷移到Android,請謹慎使用JNI。如果你要使用JNI,請學習JNI Tips
在沒有做JIT之前,使用一種確切的數據類型確實要比抽象的數據類型速度要更有效率。(例如,使用HashMap要比Map效率更高。) 有誤傳效率要高一倍,實際上只是6%左右。而且,在JIT之後,他們直接並沒有大多差異。
上面文檔中出現的數據是Android的實際運行效果。我們可以用Traceview 來測量,但是測量的數據是沒有經過JIT優化的,所以實際的效果應該是要比測量的數據稍微好些。
越來越多的黑客盯上了移動應用,每天都會增加,因為移動應用中有黑客感興趣的東西,如用戶數據。硬編碼(Hard-coded,注,固定寫死,不能修改的)安全秘鑰,SD
涉及知識點:APM, Java Agent, plugin, bytecode, asm, InvocationHandler, smail 一. 背景介紹 AP
DiskLruCache是一個十分好用的android緩存工具,我們可以從GitHub上下載其源碼:https://github.com/JakeWharton/
運算篇 1) Intro to Compute and Memory Problems Android中的Java代碼會需要經過編譯優化再執行的過程。代碼的不同寫