鍍金池/ 教程/ Android/ 代碼性能優(yōu)化建議
檢測常用的手勢
優(yōu)化layout的層級
用戶輸入
管理應用的內(nèi)存
聯(lián)系人信息
開發(fā)輔助程序
Android多媒體
添加語音功能
顯示位置地址
提供向下與橫向?qū)Ш?/span>
支持游戲控制器
訪問可穿戴數(shù)據(jù)層
處理多點觸控手勢
全屏沉浸式應用
為多線程創(chuàng)建管理器
數(shù)據(jù)保存
Intent的發(fā)送
更新Notification
優(yōu)化下載以高效地訪問網(wǎng)絡
打印
打包可穿戴應用
接收從其他App傳送來的數(shù)據(jù)
發(fā)送與接收消息
建立靈活動態(tài)的UI
處理鍵盤輸入
Building a Work Policy Controller
建立測試環(huán)境
創(chuàng)建表盤
分享文件
顯示Notification進度
實現(xiàn)自適應UI流(Flows)
使用設備管理策略增強安全性
使用能感知版本的組件
執(zhí)行網(wǎng)絡操作
建立文件分享
添加移動
更新你的Security Provider來對抗SSL漏洞利用
支持鍵盤導航
創(chuàng)建和監(jiān)視地理圍欄
發(fā)送并同步數(shù)據(jù)
使用BigView樣式
無線連接設備
提供向上導航與歷史導航
最小化定期更新造成的影響
實現(xiàn)向下的導航
支持不同的屏幕大小
Android 可穿戴應用
添加動畫
顯示聯(lián)系人頭像
使用OpenGL ES顯示圖像
處理輸入法可見性
分享文件
保持設備喚醒
淡化系統(tǒng)Bar
使用NFC分享文件
保存到Preference
Android聯(lián)系人信息與位置信息
創(chuàng)建標準的網(wǎng)絡請求
使用Drawables
管理Bitmap的內(nèi)存使用
管理Activity的生命周期
按需加載視圖
傳輸資源
為可穿戴設備創(chuàng)建自定義UI
在一個線程中執(zhí)行一段特定的代碼
性能優(yōu)化
隱藏導航欄
創(chuàng)建目錄瀏覽器
為多種大小的屏幕進行規(guī)劃
View間漸變
使用觸摸手勢
高效加載大圖
使用CursorLoader在后臺加載數(shù)據(jù)
創(chuàng)建抽屜式導航(navigation drawer)
管理音頻焦點
創(chuàng)建后臺服務
創(chuàng)建功能測試
創(chuàng)建使用Material Design的應用
停止與重啟Activity
添加一個簡便的分享功能
啟動Activity時保留導航
TV應用清單
創(chuàng)建向后兼容的UI
?# 優(yōu)化自定義View
創(chuàng)建單元測試
在UI上顯示Bitmap
建立OpenGL ES的環(huán)境
構(gòu)建表盤服務
JNI Tips
建立搜索界面
實現(xiàn)自定義View的繪制
使用HTTPS與SSL
按需操控BroadcastReceiver
分享簡單的數(shù)據(jù)
繪制形狀
Android位置信息
創(chuàng)建并運行可穿戴應用
執(zhí)行 Sync Adpater
獲取最后可知位置
創(chuàng)建 Android 項目
實現(xiàn)高效的導航
退出全屏的Activity
創(chuàng)建Card
兼容音頻輸出設備
同步數(shù)據(jù)單元
傳輸數(shù)據(jù)時避免消耗大量電量
保存到文件
緩存Bitmap
提供配置 Activity
調(diào)度重復的鬧鐘
實現(xiàn)輔助功能
重復的下載是冗余的
隱藏狀態(tài)欄
實現(xiàn)自定義的網(wǎng)絡請求
規(guī)劃界面和他們之間的關(guān)系
使用Sync Adapter傳輸數(shù)據(jù)
TV應用內(nèi)搜索
響應觸摸事件
使用Google Cloud Messaging(已廢棄)
控制相機
Android網(wǎng)絡連接與云服務
請求分享一個文件
處理TV硬件
響應UI可見性的變化
使用網(wǎng)絡服務發(fā)現(xiàn)
指定輸入法類型
優(yōu)化電池壽命
創(chuàng)建TV應用
獲取聯(lián)系人列表
拖拽與縮放
啟動與停止線程池中的線程
創(chuàng)建 Sync Adpater
使用 WiFi P2P 服務發(fā)現(xiàn)
開始使用Material Design
代理至新的APIs
使用include標簽重用layouts
使得View可交互
高效顯示Bitmap
創(chuàng)建企業(yè)級應用
Fragments之間的交互
創(chuàng)建與執(zhí)行測試用例
綜合:設計我們的樣例 App
繪制表盤
建立簡單的用戶界面
自定義動畫
開發(fā)輔助服務
避免出現(xiàn)程序無響應ANR(Keeping Your App Responsive)
使用ViewPager實現(xiàn)屏幕滑動
設計高效的導航
Android分享操作(Building Apps with Content Sharing)
提供向后的導航
保持向下兼容
創(chuàng)建TV播放應用
縮放View
使用 WiFi 建立 P2P 連接
Android后臺任務
連接到網(wǎng)絡
為 Notification 添加頁面
使TV應用是可被搜索的
添加Action Bar
使用Material的主題
啟動另一個Activity
顯示正在播放卡片
適配不同的系統(tǒng)版本
輕松錄制視頻
創(chuàng)建可穿戴的應用
創(chuàng)建自定義的布局
重新創(chuàng)建Activity
使用CursorLoader執(zhí)行查詢?nèi)蝿?/span>
使用舊的APIs實現(xiàn)新API的效果
使用備份API
安全要點
Android入門基礎:從這里開始
保存并搜索數(shù)據(jù)
根據(jù)網(wǎng)絡連接類型來調(diào)整下載模式
使用Tabs創(chuàng)建Swipe視圖
SMP(Symmetric Multi-Processor) Primer for Android
解析 XML 數(shù)據(jù)
使用 Volley 傳輸網(wǎng)絡數(shù)據(jù)
建立ActionBar
Android交互設計
使用Intent修改聯(lián)系人信息
增加搜索功能
輕松拍攝照片
定義形狀
測試你的Activity
在 Notifcation 中接收語音輸入
與其他應用的交互
管理系統(tǒng)UI
追蹤手勢移動
Android界面設計
執(zhí)行 Android 程序
顯示確認界面
創(chuàng)建Lists與Cards
打印HTML文檔
創(chuàng)建TV應用
為多屏幕設計
定義Shadows與Clipping視圖
使用Fragment建立動態(tài)UI
接收Activity返回的結(jié)果
布局變更動畫
定位常見的問題
自定義ActionBar的風格
定義Layouts
發(fā)送簡單的網(wǎng)絡請求
啟動與銷毀Activity
與UI線程通信
非UI線程處理Bitmap
創(chuàng)建TV布局
提升Layout的性能
報告任務執(zhí)行狀態(tài)
判斷并監(jiān)測網(wǎng)絡連接狀態(tài)
兼容不同的設備
處理按鍵動作
優(yōu)化性能和電池使用時間
給其他App發(fā)送簡單的數(shù)據(jù)
Implementing App Restrictions
向后臺服務發(fā)送任務請求
展示Card翻轉(zhuǎn)動畫
管理ViewGroup中的觸摸事件
兼容不同的屏幕密度
通過藍牙進行調(diào)試
為可穿戴設備創(chuàng)建Notification
控制音量與音頻播放
獲取聯(lián)系人詳情
在表盤上顯示信息
提供向上的導航
滾動手勢動畫
幫助用戶在TV上找到內(nèi)容
創(chuàng)建TV導航
為索引指定App內(nèi)容
ActionBar的覆蓋疊加
Android Wear 上的位置檢測
保護安全與隱私的最佳策略
Ensuring Compatibility with Managed Profiles
解決云同步的保存沖突
獲取位置更新
創(chuàng)建List
測試程序
管理網(wǎng)絡的使用情況
為App內(nèi)容開啟深度鏈接
推薦TV內(nèi)容
建立一個Notification
管理音頻播放
設計表盤
拍照
處理控制器輸入動作
判斷并監(jiān)測設備的底座狀態(tài)與類型
處理查詢的結(jié)果
保存到數(shù)據(jù)庫
支持多個游戲控制器
創(chuàng)建 Stub Content Provider
使得ListView滑動順暢
處理數(shù)據(jù)層的事件
創(chuàng)建TV應用的第一步
使得你的App內(nèi)容可被Google搜索
將 Notification 放成一疊
創(chuàng)建 Stub 授權(quán)器
暫停與恢復Activity
管理設備的喚醒狀態(tài)
Android圖像與動畫
打印照片
云同步
創(chuàng)建TV直播應用
為Notification賦加可穿戴特性
提供一個Card視圖
建立請求隊列(RequestQueue)
適配不同的語言
創(chuàng)建詳情頁
測試UI組件
接收其他設備的文件
創(chuàng)建自定義View
建立第一個App
創(chuàng)建2D Picker
監(jiān)測電池的電量與充電狀態(tài)
打印自定義文檔
抽象出新的APIs
通知提示用戶
獲取文件信息
運用投影與相機視角
在IntentService中執(zhí)行后臺任務
多線程操作
創(chuàng)建一個Fragment
添加Action按鈕
在不同的 Android 系統(tǒng)版本支持控制器
維護兼容性
發(fā)送文件給其他設備
創(chuàng)建TV游戲應用
創(chuàng)建自定義的View類
代碼性能優(yōu)化建議
Intent過濾
適配不同的屏幕

代碼性能優(yōu)化建議

編寫:kesenhoo - 原文:http://developer.android.com/training/articles/perf-tips.html

這篇文章主要介紹一些小細節(jié)的優(yōu)化技巧,雖然這些小技巧不能較大幅度的提升應用性能,但是恰當?shù)倪\用這些小技巧并發(fā)生累積效應的時候,對于整個App的性能提升還是有不小作用的。通常來說,選擇合適的算法與數(shù)據(jù)結(jié)構(gòu)會是你首要考慮的因素,在這篇文章中不會涉及這方面的知識點。你應該使用這篇文章中的小技巧作為平時寫代碼的習慣,這樣能夠提升代碼的效率。

通常來說,高效的代碼需要滿足下面兩個原則:

  • 不要做冗余的工作
  • 盡量避免執(zhí)行過多的內(nèi)存分配操作

在優(yōu)化App時其中一個難點就是讓App能在各種型號的設備上運行。不同版本的虛擬機在不同的處理器上會有不同的運行速度。你甚至不能簡單的認為“設備X的速度是設備Y的F倍”,然后還用這種倍數(shù)關(guān)系去推測其他設備。另外,在模擬器上的運行速度和在實際設備上的速度沒有半點關(guān)系。同樣,設備有沒有JIT也對運行速度有重大影響:在有JIT情況下的最優(yōu)化代碼不一定在沒有JIT的情況下也是最優(yōu)的。

為了確保App在各設備上都能良好運行,就要確保你的代碼在不同檔次的設備上都盡可能的優(yōu)化。

避免創(chuàng)建不必要的對象

創(chuàng)建對象從來不是免費的。Generational GC可以使臨時對象的分配變得廉價一些,但是執(zhí)行分配內(nèi)存總是比不執(zhí)行分配操作更昂貴。

隨著你在App中分配更多的對象,你可能需要強制gc,而gc操作會給用戶體驗帶來一點點卡頓。雖然從Android 2.3開始,引入了并發(fā)gc,它可以幫助你顯著提升gc的效率,減輕卡頓,但畢竟不必要的內(nèi)存分配操作還是應該盡量避免。

因此請盡量避免創(chuàng)建不必要的對象,有下面一些例子來說明這個問題:

  • 如果你需要返回一個String對象,并且你知道它最終會需要連接到一個StringBuffer,請修改你的函數(shù)實現(xiàn)方式,避免直接進行連接操作,應該采用創(chuàng)建一個臨時對象來做字符串的拼接這個操作。
  • 當從已經(jīng)存在的數(shù)據(jù)集中抽取出String的時候,嘗試返回原數(shù)據(jù)的substring對象,而不是創(chuàng)建一個重復的對象。使用substring的方式,你將會得到一個新的String對象,但是這個string對象是和原string共享內(nèi)部char[]空間的。

一個稍微激進點的做法是把所有多維的數(shù)據(jù)分解成一維的數(shù)組:

  • 一組int數(shù)據(jù)要比一組Integer對象要好很多??梢缘弥?,兩組一維數(shù)組要比一個二維數(shù)組更加的有效率。同樣的,這個道理可以推廣至其他原始數(shù)據(jù)類型。
  • 如果你需要實現(xiàn)一個數(shù)組用來存放(Foo,Bar)的對象,記住使用Foo[]與Bar[]要比(Foo,Bar)好很多。(例外的是,為了某些好的API的設計,可以適當做一些妥協(xié)。但是在自己的代碼內(nèi)部,你應該多多使用分解后的容易)。

通常來說,需要避免創(chuàng)建更多的臨時對象。更少的對象意味者更少的gc動作,gc會對用戶體驗有比較直接的影響。

選擇Static而不是Virtual

如果你不需要訪問一個對象的值,請保證這個方法是static類型的,這樣方法調(diào)用將快15%-20%。這是一個好的習慣,因為你可以從方法聲明中得知調(diào)用無法改變這個對象的狀態(tài)。

常量聲明為Static Final

考慮下面這種聲明的方式

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

編譯器會使用一個初始化類的函數(shù),然后當類第一次被使用的時候執(zhí)行。這個函數(shù)將42存入intVal,還從class文件的常量表中提取了strVal的引用。當之后使用intValstrVal的時候,他們會直接被查詢到。

我們可以用final關(guān)鍵字來優(yōu)化:

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

這時再也不需要上面的方法了,因為final聲明的常量進入了靜態(tài)dex文件的域初始化部分。調(diào)用intVal的代碼會直接使用42,調(diào)用strVal的代碼也會使用一個相對廉價的“字符串常量”指令,而不是查表。

Notes:這個優(yōu)化方法只對原始類型和String類型有效,而不是任意引用類型。不過,在必要時使用static final是個很好的習慣。

避免內(nèi)部的Getters/Setters

像C++等native language,通常使用getters(i = getCount())而不是直接訪問變量(i = mCount)。這是編寫C++的一種優(yōu)秀習慣,而且通常也被其他面向?qū)ο蟮恼Z言所采用,例如C#與Java,因為編譯器通常會做inline訪問,而且你需要限制或者調(diào)試變量,你可以在任何時候在getter/setter里面添加代碼。

然而,在Android上,這不是一個好的寫法。虛函數(shù)的調(diào)用比起直接訪問變量要耗費更多。在面向?qū)ο缶幊讨校瑢etter和setting暴露給公用接口是合理的,但在類內(nèi)部應該僅僅使用域直接訪問。

在沒有JIT(Just In Time Compiler)時,直接訪問變量的速度是調(diào)用getter的3倍。有JIT時,直接訪問變量的速度是通過getter訪問的7倍。

請注意,如果你使用ProGuard,你可以獲得同樣的效果,因為ProGuard可以為你inline accessors.

使用增強的For循環(huán)

增強的For循環(huán)(也被稱為 for-each 循環(huán))可以被用在實現(xiàn)了 Iterable 接口的 collections 以及數(shù)組上。使用collection的時候,Iterator會被分配,用于for-each調(diào)用hasNext()next()方法。使用ArrayList時,手寫的計數(shù)式for循環(huán)會快3倍(不管有沒有JIT),但是對于其他collection,增強的for-each循環(huán)寫法會和迭代器寫法的效率一樣。

請比較下面三種循環(huán)的方法:

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沒有辦法對它進行優(yōu)化。
  • one()稍微快些。
  • two() 在沒有做JIT時是最快的,可是如果經(jīng)過JIT之后,與方法one()是差不多一樣快的。它使用了增強的循環(huán)方法for-each。

所以請盡量使用for-each的方法,但是對于ArrayList,請使用方法one()。

Tips:你還可以參考 Josh Bloch 的 《Effective Java》這本書的第46條

使用包級訪問而不是內(nèi)部類的私有訪問

參考下面一段代碼

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

這里重要的是,我們定義了一個私有的內(nèi)部類(Foo$Inner),它直接訪問了外部類中的私有方法以及私有成員對象。這是合法的,這段代碼也會如同預期一樣打印出"Value is 27"。

問題是,VM因為FooFoo$Inner是不同的類,會認為在Foo$Inner中直接訪問Foo類的私有成員是不合法的。即使Java語言允許內(nèi)部類訪問外部類的私有成員。為了去除這種差異,編譯器會產(chǎn)生一些仿造函數(shù):

/*package*/ static int Foo.access$100(Foo foo) {
    return foo.mValue;
}
/*package*/ static void Foo.access$200(Foo foo, int value) {
    foo.doStuff(value);
}

每當內(nèi)部類需要訪問外部類中的mValue成員或需要調(diào)用doStuff()函數(shù)時,它都會調(diào)用這些靜態(tài)方法。這意味著,上面的代碼可以歸結(jié)為,通過accessor函數(shù)來訪問成員變量。早些時候我們說過,通過accessor會比直接訪問域要慢。所以,這是一個特定語言用法造成性能降低的例子。

如果你正在性能熱區(qū)(hotspot:高頻率、重復執(zhí)行的代碼段)使用像這樣的代碼,你可以把內(nèi)部類需要訪問的域和方法聲明為包級訪問,而不是私有訪問權(quán)限。不幸的是,這意味著在相同包中的其他類也可以直接訪問這些域,所以在公開的API中你不能這樣做。

避免使用float類型

Android系統(tǒng)中float類型的數(shù)據(jù)存取速度是int類型的一半,盡量優(yōu)先采用int類型。

就速度而言,現(xiàn)代硬件上,float 和 double 的速度是一樣的??臻g而言,double 是兩倍float的大小。在空間不是問題的情況下,你應該使用 double 。

同樣,對于整型,有些處理器實現(xiàn)了硬件幾倍的乘法,但是沒有除法。這時,整型的除法和取余是在軟件內(nèi)部實現(xiàn)的,這在你使用哈希表或大量計算操作時要考慮到。

使用庫函數(shù)

除了那些常見的讓你多使用自帶庫函數(shù)的理由以外,記得系統(tǒng)函數(shù)有時可以替代第三方庫,并且還有匯編級別的優(yōu)化,他們通常比帶有JIT的Java編譯出來的代碼更高效。典型的例子是:Android API 中的 String.indexOf(),Dalvik出于內(nèi)聯(lián)性能考慮將其替換。同樣 System.arraycopy()函數(shù)也被替換,這樣的性能在Nexus One測試,比手寫的for循環(huán)并使用JIT還快9倍。

Tips:參見 Josh Bloch 的 《Effective Java》這本書的第47條

謹慎使用native函數(shù)

結(jié)合Android NDK使用native代碼開發(fā),并不總是比Java直接開發(fā)的效率更好的。Java轉(zhuǎn)native代碼是有代價的,而且JIT不能在這種情況下做優(yōu)化。如果你在native代碼中分配資源(比如native堆上的內(nèi)存,文件描述符等等),這會對收集這些資源造成巨大的困難。你同時也需要為各種架構(gòu)重新編譯代碼(而不是依賴JIT)。你甚至對已同樣架構(gòu)的設備都需要編譯多個版本:為G1的ARM架構(gòu)編譯的版本不能完全使用Nexus One上ARM架構(gòu)的優(yōu)勢,反之亦然。

Native 代碼是在你已經(jīng)有本地代碼,想把它移植到Android平臺時有優(yōu)勢,而不是為了優(yōu)化已有的Android Java代碼使用。

如果你要使用JNI,請學習JNI Tips

Tips:參見 Josh Bloch 的 《Effective Java》這本書的第54條

關(guān)于性能的誤區(qū)

在沒有JIT的設備上,使用一種確切的數(shù)據(jù)類型確實要比抽象的數(shù)據(jù)類型速度要更有效率(例如,調(diào)用HashMap map要比調(diào)用Map map效率更高)。有誤傳效率要高一倍,實際上只是6%左右。而且,在JIT之后,他們直接并沒有大多差異。

在沒有JIT的設備上,讀取緩存域比直接讀取實際數(shù)據(jù)大概快20%。有JIT時,域讀取和本地讀取基本無差。所以優(yōu)化并不值得除非你覺得能讓你的代碼更易讀(這對 final, static, static final 域同樣適用)。

關(guān)于測量

在優(yōu)化之前,你應該確定你遇到了性能問題。你應該確保你能夠準確測量出現(xiàn)在的性能,否則你也不會知道優(yōu)化是否真的有效。

本章節(jié)中所有的技巧都需要Benchmark(基準測試)的支持。Benchmark可以在 code.google.com "dalvik" project 中找到

Benchmark是基于Java版本的 Caliper microbenchmarking框架開發(fā)的。Microbenchmarking很難做準確,所以Caliper幫你完成這部分工作,甚至還幫你測了你沒想到需要測量的部分(因為,VM幫你管理了代碼優(yōu)化,你很難知道這部分優(yōu)化有多大效果)。我們強烈推薦使用Caliper來做你的基準微測工作。

我們也可以用Traceview 來測量,但是測量的數(shù)據(jù)是沒有經(jīng)過JIT優(yōu)化的,所以實際的效果應該是要比測量的數(shù)據(jù)稍微好些。

關(guān)于如何測量與調(diào)試,還可以參考下面兩篇文章:

上一篇:支持游戲控制器下一篇:測試程序