安卓效能最佳化

來源:互聯網
上載者:User

標籤:

Google效能點滴

http://developer.android.com/intl/zh-cn/training/articles/perf-tips.html

本文主要涉及一些小最佳化,組合使用可以提升App整體效能,但不會顯著的提升效能。提升效能首選合適的演算法和資料結構,這超出了本文的範疇。這裡的技巧應該作為你平時寫代碼的習慣,以便寫出高效的代碼。

高效的代碼有兩個基本的規則:

  • 不做不必要的事
  • 盡量不分配記憶體

最佳化Android程式的時候需要面對不同的硬體、不同版本的VM,不同的處理器,不同的速度!模擬器上的測試很少涉及效能。是否擁有JIT影響也很大。

避免建立不必要的對象

對象建立不是免費的。垃圾收集也是需要成本的。

如果方法返回String,結果總會被添加到StringBuffer,應該修改函數實現簽名不建立臨時對象直接附加 。
當從一組輸入資料中抽取字串的時候,返回substring而不是建立拷貝. 這樣雖然建立了新的String對象,但是它和輸入資料共用了char[](弊端是如果僅僅使用來源資料的一小部分,會導致整個資料無法回收。)。

一些更激進的做法是把多維陣列切割成多個一維數組:int[]比Integer[]更高效。

使用Static而不是Virtual

如果不需要訪問對象的成員,把方法聲明成 static,調用速度將提高15%-20%。這是一個很好的習慣,因為從方法簽名上可以辨別出這個方法調用不會影響對象的狀態。

使用Static Final定義常量
static int intVal = 42;static String strVal = "Hello, world!";

 

編譯器會產生一個類初始化方法 <clinit>,類首次使用的時候調用。該方法把42存入了intVal,並且從類檔案字串常量表中把引用賦給strVal。後面使用這些值時,用欄位尋找(field lookup)的方式。

加上final :

static final int intVal = 42;static final String strVal = "Hello, world!";

 

這樣類不再需要一個 <clinit> 方法,因為常量會進入dex檔案的靜態欄位初始化器(static field initializers)。引用intVal會被42直接代替。

Note: 這項最佳化僅對基本類型和String類型有效,而不是任意的參考型別。但是在聲明常量的時候加上static final首碼依然是個好習慣。

避免內部Getters/Setters

C++之類的原生語言常使用getters( i = getCount() )替換成員訪問( i = mCount)是, 這樣編譯器可以內聯訪問,控制存取權限。但Android上虛方法(virtual method)的訪問比起成員尋找(field lookup)還貴。

沒有JIT的成員訪問比用getter方法訪問大約快3倍。有JIT訪問成員變數和訪問本地變數一樣廉價,成員訪問大約比getter訪問速度快7倍。

注意ProGuard可以內聯代碼。

使用增強版For迴圈

增強版For迴圈(也叫做 "for-each" 迴圈) 可以用來枚舉實現了Iterable介面的集合和數組。集合分配迭代器(iterator)對象來實現 hasNext() 和 next() 方法。 ArrayList手寫的帶計數器的迴圈要快3 倍,其它集合則效率差不多。

下面是幾種常見迭代數組的情境:

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迴圈。

 

使用包級訪問而不是內部類的私人訪問

對於下面的代碼

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$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);}

 

每次Foo$Inner訪問Foo的私人成員或者私人方法時,表面上是直接調用,但是實際上是通過這些靜態存取方法訪問的,方法調用會比成員的直接調用慢很多。定義成員變數的時候最好改成 public 或者 default, 但是這樣又會暴露類的成員,所以在公開的API裡面不要這麼做。


避免使用浮點數

Android 裝置上float比int會慢2倍。

在現在的硬體裝置上float和double類型速度上沒有差別,但後者佔用了2倍的空間,對於台式機空間並不是問題,但Android是個問題。

另外,對於整型數有些機器是有硬體乘法的,但是卻沒有除法。除法或者取餘運算都是軟體實現的,如果你在設計雜湊表或者做了很多數學運算的時候需要考慮這些。

 

使用庫函數

優先考慮庫(library)中提供的代碼,有時系統可以使用彙編代碼(hand-coded assembler)來替換庫方法,比使用JIT更高效,比如String.indexOf() 和相關的API,Dalvik會使用內聯實現。同樣的 System.arraycopy() 在帶有JIT的nexus裝置上比手寫迴圈快9倍。

 

謹慎使用native函數

使用基於NDK的原生代碼未必比Java更快,因為在java-native之前的轉換也是需要成本的,而且JIT也無法最佳化這種邊界情況。如果你使用了本地資源(native-resources,記憶體、檔案或其它),資源的及時回收將會變得更加困難。而且你還要把本地代碼編譯成各種架。NDK一般是用來遷移原有的代碼到Android平台的,並不是為了“加速”Java。

如果的確需要使用native代碼,看看這裡 JNI Tips。

關於效能的誤區

在沒有JIT的裝置上,通過具體的類型調用方法比使用介面來調用方法更快(比如調用 HashMap map 比 Map map 更快,雖然他們實際上都是調用 HashMap 的方法)。之前有說大約會慢1倍,但是事實上只是慢了 6% 左右,而在JIT裝置上幾乎就沒有區別了。

在沒有JIT的裝置商,訪問緩衝成員大概比重複的訪問快20%,但是對於JIT,成員訪問和本地變數幾乎沒有差別,所以這項最佳化沒有太大的意思,除非這樣讓你的代碼可讀性更好。(同樣適用於用static、final和 static final修飾的變數)

 

持續度量

在你開始最佳化之前,請確保你有效能問題需要解決,確保準確的衡量當前的效能,否則你無法弄清最佳化帶來了多少效能的提升。

這些標準測試是基於Java版的 Caliper 微測試(Microbenchmarks)架構。Microbenchmarks 很難做到準確,所以 Caliper 幫我們做了這些工作,甚至會檢測到一些我們認為測試了但是沒有測試的情況(因為VM對代碼做了最佳化)。強烈建議使用 Caliper 來運行自己的微測試程式。

使用 Traceview 也是很有用的,需要記住的是目前 Traceview 是不啟動JIT的,這樣它會錯估JIT帶來的時間最佳化。按照Traceview的資料做出變動之後,實際的代碼在沒有Traceview的時候可以啟動並執行更快。

 

安卓效能最佳化

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.