自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

官方文檔:Android應(yīng)用程序運(yùn)行的性能設(shè)計(jì)

移動(dòng)開發(fā) Android
本文為iteye的熱心網(wǎng)友a(bǔ)dmires對(duì)Android官方文檔進(jìn)行的翻譯,將講述《Android應(yīng)用程序運(yùn)行的性能設(shè)計(jì)》:Android應(yīng)用程序運(yùn)行的移動(dòng)設(shè)備受限于其運(yùn)算能力,存儲(chǔ)空間,及電池續(xù)航。

Android應(yīng)用程序運(yùn)行的移動(dòng)設(shè)備受限于其運(yùn)算能力,存儲(chǔ)空間,及電池續(xù)航。由此,它必須是高效的。電池續(xù)航可能是一個(gè)促使你優(yōu)化程序的原因,即使他看起來已經(jīng)運(yùn)行的足夠快了。由于續(xù)航對(duì)用戶的重要性,當(dāng)電量耗損陡增時(shí),意味這用戶遲早會(huì)發(fā)現(xiàn)是由于你的程序。

雖然這份文檔主要包含著細(xì)微的優(yōu)化,但這些絕不能成為你軟件成敗的關(guān)鍵。選擇合適的算法和數(shù)據(jù)結(jié)構(gòu)永遠(yuǎn)是你最先應(yīng)該考慮的事情,但這超出這份文檔之外。

1.介紹

寫出高效的代碼有兩條基本的原則:

◆不作沒有必要的工作

◆盡量避免內(nèi)存分配。

2.明智的優(yōu)化

這份文檔是關(guān)于Android規(guī)范的細(xì)微優(yōu)化,所以先確保你已經(jīng)了解哪些代碼需要優(yōu)化,并且知道如何去衡量你所做修改所帶來的效果(好或壞)。用開投資開發(fā)的時(shí)間是有限的,所以明智的時(shí)間規(guī)劃很重要。

這份文檔同時(shí)確保你在算法和數(shù)據(jù)結(jié)構(gòu)上作出最佳選擇,同時(shí)考慮了API選擇所帶來的潛在影響。使用恰當(dāng)?shù)臄?shù)據(jù)結(jié)構(gòu)和算法比這里的任何建議都有價(jià)值,考慮API版本帶來的影響會(huì)如實(shí)你選擇更好的實(shí)現(xiàn)。

當(dāng)你優(yōu)化Android程序是會(huì)遇到的一個(gè)棘手問題是確保你的程序能在不同的硬件平臺(tái)上運(yùn)行。不同版本的虛擬機(jī)在不同處理器上的運(yùn)行速度各不相同。并且不是簡單的設(shè)備A比設(shè)備B快或者慢,并針對(duì)一個(gè)設(shè)備與其他設(shè)備之間做出排列。特別的,模擬器上只能評(píng)測小部分可以在設(shè)備上體現(xiàn)的東西。有無JIT的設(shè)備間也有著巨大差異:對(duì)于有JIT設(shè)備好的代碼有時(shí)對(duì)無JIT的設(shè)備并不是最好的。

如果你想知道程序在設(shè)備上的表現(xiàn),就必須在上面進(jìn)行測試

3.避免創(chuàng)建不必要的對(duì)象

對(duì)象創(chuàng)建永遠(yuǎn)不會(huì)免費(fèi)的。每個(gè)線程的分代GC給零時(shí)對(duì)象分配一個(gè)地址池能降低分配開銷,但分配內(nèi)存往往需要比不分配內(nèi)存高的代價(jià)。

如果在用戶界面周期內(nèi)分配對(duì)象,會(huì)強(qiáng)制一個(gè)周期性的垃圾回收,給用戶體驗(yàn)造成小小的停頓間隙。Gingerbread中介紹的并發(fā)回收也許有用,但應(yīng)該避免不必要的工作。

因此,避免創(chuàng)建不需要的對(duì)象實(shí)例。下面是幾個(gè)例子:

◆如果有一個(gè)返回String的方法,他的返回值通常附加在一個(gè)StringBuffer上,改變聲明和實(shí)現(xiàn),這樣函數(shù)直接在其后面附加,而非創(chuàng)建一個(gè)短暫存在的零時(shí)變量。

◆當(dāng)從輸入的數(shù)據(jù)集合中讀取數(shù)據(jù)是,考慮返回原始數(shù)據(jù)的子串,而非新建一哥拷貝.這樣你會(huì)創(chuàng)建一個(gè)新的對(duì)象,但是他們共享該數(shù)據(jù)的char數(shù)組。換來的是即使你僅僅使用原始輸入的一部分,你也需要保證它一直存在于內(nèi)存中。

一個(gè)更徹底的觀點(diǎn)是將多維數(shù)組切割成一維數(shù)組:

◆Int類型的數(shù)組比Integer類型的好。推而廣之,兩個(gè)平行的int數(shù)組要比一個(gè)(int,int)型的對(duì)象數(shù)組高效。這個(gè)定理對(duì)于任何基本數(shù)據(jù)類型的組合都通用。

◆如果需要實(shí)現(xiàn)存放元組(Foo,Bar)對(duì)象的容器,記住兩個(gè)平行數(shù)組Foo[],Bar[]會(huì)優(yōu)于一個(gè)(Foo,Bar)對(duì)象的數(shù)組。(例外情況是:當(dāng)你設(shè)計(jì)API給其他代碼調(diào)用時(shí),最好用好的API設(shè)計(jì)來換取小的速度提升。但在自己的內(nèi)部代碼中,盡量嘗試高效的實(shí)現(xiàn)。)

通常來說,盡量避免創(chuàng)建短時(shí)零時(shí)對(duì)象.少的對(duì)象創(chuàng)建意味著低頻的垃圾回收。這對(duì)于用戶體驗(yàn)產(chǎn)生直接的影響。

4.性能之謎

前一個(gè)版本的文檔給出了好多誤導(dǎo)人的主張,這里做一些澄清:

◆在沒有JIT的設(shè)備上,調(diào)用方法所傳遞的對(duì)象采用具體的類型而非接口類型會(huì)更有效(比如,傳遞HashMap map比傳遞Map map調(diào)用一個(gè)方法耗費(fèi)的開銷小,盡管兩種情況下的map都是HashMap).但這并不是兩倍慢的情形,事實(shí)上,只相差6%,而JIT使這兩種調(diào)用的效率不分伯仲。

◆在沒有JIT的設(shè)備上,訪問緩存后的字段比直接訪問字段快大概20%。在有JIT的情況下,字段訪問和局部訪問耗費(fèi)是一樣的 。所以這里不值得優(yōu)化,除非你覺得他會(huì)讓你的代碼更易讀(對(duì)于final,static,及static final 變量同樣適用).

5.用靜態(tài)代替虛擬

如果不需要訪問某對(duì)象的字段,將方法設(shè)置為靜態(tài),調(diào)用會(huì)加速15%到20%。這也是一種好的做法,因?yàn)槟憧梢酝ㄟ^方法聲明知曉調(diào)用該方法不需要更新此對(duì)象的狀態(tài)。

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

在源生語言像C++中,通常做法是用Getters(i=getCount())代替直接訪問字段(i=mCount)。這是C++中一個(gè)好的習(xí)慣,因?yàn)榫幾g器會(huì)內(nèi)聯(lián)這些訪問,如果需要約束或者調(diào)試這些域的訪問,你可以在任何時(shí)間添加代碼。

在Android中,這是個(gè)不好的想法。虛方法調(diào)用代價(jià)比直接存取字段高昂的多。按照通常面向?qū)ο笳Z言的做法在公共接口中使用Getters和Setters是有原因的,但應(yīng)該在一個(gè)經(jīng)常訪問其字段的類中采用直接訪問。

無JIT時(shí),直接字段訪問大約比調(diào)用無關(guān)緊要的getter來訪問快3倍。有JIT時(shí)(直接訪問字段開銷和訪問局部變量是一樣的),要快7倍。在Froyo版本中確實(shí)如此,但以后會(huì)在JIT中改進(jìn)Getter方法的內(nèi)聯(lián)。

7.對(duì)常量使用Static Final修飾符

考慮下面類首的聲明:

Java代碼

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

編譯器生成一個(gè)類初始化方法<clinit>,當(dāng)類初次被使用時(shí)執(zhí)行,這個(gè)方法將42存入intVal中,并得到類字符串常量strVal的引用。當(dāng)這些值在后面被引用時(shí),他們通過字段查找進(jìn)行訪問。

我們改進(jìn)實(shí)現(xiàn),采用 final關(guān)鍵字:

Java代碼 

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

類不再需要<clinit>方法,因?yàn)槌A窟M(jìn)入了dex文件中的靜態(tài)字段初始化器中。引用intVal的代碼,直接調(diào)用整形值42,而訪問strVal時(shí)也會(huì)采用相對(duì)開銷較小的 “string constant”(字符串常量)指令替代字段查找。(這種優(yōu)化僅僅是針對(duì)基本數(shù)據(jù)類型和String類型常量的,而非任意的引用類型。但盡可能的將常量聲明為static final類型是一種好的做法。

8.使用改進(jìn)的For循環(huán)語法

改進(jìn)的for循環(huán)(有時(shí)被稱為“for-each”循環(huán))能夠用于實(shí)現(xiàn)了iterable接口的集合類及數(shù)組中。在集合類中,迭代器促使接口訪問hasNext()和next()方法,在ArrayList中,計(jì)數(shù)循環(huán)迭代要快3倍(無論有沒有JIT),但其他集合類中,改進(jìn)的for循環(huán)語法和迭代器具有相同的效率。

這里有一些迭代數(shù)組的實(shí)現(xiàn):

Java代碼 

  1. static class Foo {    
  2.         int mSplat;    
  3.     }    
  4.     Foo[] mArray = ...    
  5.     
  6.     public void zero() {    
  7.         int sum = 0;    
  8.         for (int i = 0; i < mArray.length; ++i) {    
  9.             sum += mArray[i].mSplat;    
  10.         }    
  11.     }    
  12.     
  13.     public void one() {    
  14.         int sum = 0;    
  15.         Foo[] localArray = mArray;    
  16.         int len = localArray.length;    
  17.     
  18.         for (int i = 0; i < len; ++i) {    
  19.             sum += localArray[i].mSplat;    
  20.         }    
  21.     }    
  22.     
  23.     public void two() {    
  24.         int sum = 0;    
  25.         for (Foo a : mArray) {    
  26.             sum += a.mSplat;    
  27.         }    
  28.     }    

zero()是當(dāng)中最慢的,因?yàn)閷?duì)于這個(gè)遍歷中的歷次迭代,JIT不能優(yōu)化獲取數(shù)組長度的開銷。

One()稍快,將所有東西都放進(jìn)局部變量中,避免了查找。但僅只有數(shù)組長度促使了性能的改善。

Two()是在無JIT的設(shè)備上運(yùn)行最快的,對(duì)于有JIT的設(shè)備則和one()不分上下。他采用了JDK1.5中的改進(jìn)for循環(huán)語法。

結(jié)論:優(yōu)先采用改進(jìn)的for循環(huán),但在性能要求苛刻的ArrayList迭代中考慮采用手寫計(jì)數(shù)循環(huán)

9.在私有內(nèi)部內(nèi)中,考慮用包訪問權(quán)限替代私有訪問權(quán)限

考慮下面的定義:

Java代碼

  1. public class Foo {    
  2.     private class Inner {    
  3.         void stuff() {    
  4.             Foo.this.doStuff(Foo.this.mValue);    
  5.         }    
  6.     }    
  7.     
  8.     private int mValue;    
  9.     
  10.     public void run() {    
  11.         Inner in = new Inner();    
  12.         mValue = 27;    
  13.         in.stuff();    
  14.     }    
  15.     
  16.     private void doStuff(int value) {    
  17.         System.out.println("Value is " + value);    
  18.     }    
  19. }    

需要注意的關(guān)鍵是:我們定義的一個(gè)私有內(nèi)部類(Foo$Inner)直接訪問外部類中的一個(gè)私有方法和私有變量。這是合法的,代碼也會(huì)打印出預(yù)期的“Value is 27”。

但問題是虛擬機(jī)認(rèn)為從Foo$Inner中直接訪問Foo的私有成員是非法的,因?yàn)樗麄兪莾蓚€(gè)不同的類,盡管Java語言允許內(nèi)部類訪問外部類的私有成員,編譯器生成幾個(gè)綜合方法來橋接這些間隙。

Java代碼

  1. /*package*/ static int Foo.access$100(Foo foo) {    
  2.     return foo.mValue;    
  3. }    
  4. /*package*/ static void Foo.access$200(Foo foo, int value) {    
  5.     foo.doStuff(value);    
  6. }    

內(nèi)部類會(huì)在外部類中任何需要訪問mValue字段或者doStuff方法的地方調(diào)用這些靜態(tài)方法。這意味著這些代碼將直接存取成員變量歸結(jié)為通過存取器方法訪問。之前提到存取器訪問如何比直接訪問慢,這例子說明,某些語言約定導(dǎo)致了不可見的性能問題。

如果你在高性能的Hotspot中使用這些代碼,可以通過聲明被內(nèi)部類訪問的字段和成員為包訪問權(quán)限,而非私有。不幸的是這意味著這些字段會(huì)被其他處于同一個(gè)包中的類訪問,因此在公共API中不宜采用。

10. 合理利用浮點(diǎn)數(shù)

通常的經(jīng)驗(yàn)是,在Android設(shè)備中,浮點(diǎn)數(shù)會(huì)比整型慢兩倍,在缺少FPU,或是JIT的G1以及有FPU和JIT的Nexus One中確實(shí)如此(兩種設(shè)備間算數(shù)運(yùn)算的絕對(duì)速度差大約是10倍).

速度術(shù)語中,在現(xiàn)代硬件上,float和double之間并沒有不同。更廣泛的講,double大約2倍大。在沒有存儲(chǔ)空間問題的桌面機(jī)器中,double的優(yōu)先級(jí)高于float。

但即使是整型,有些芯片擁有硬件乘法,卻缺少除法。這種情況下,整型除法和求模運(yùn)算是通過軟件實(shí)現(xiàn)的,考慮下當(dāng)你設(shè)計(jì)Hash表,或是做大量的算術(shù)。

11.了解并使用類庫

除了通常的那些有限選擇類庫代碼而非自己的原因外,考慮到系統(tǒng)空閑時(shí)用手寫的匯編程序來替代類庫方法,這可能比JIT中能生成的最好的等效Java代碼還要好。典型的例子就是String.indexOf,Dalvik用用內(nèi)部內(nèi)聯(lián)來替代。同樣的,System.arraycopy方法比Nexus One中有JIT的自行編碼循環(huán)快9倍.

12.合理利用本地方法

本地方法并不是一定比Java高效,至少,Java和native之間過渡的關(guān)聯(lián)是有消耗的。而JIT并不能越過這個(gè)界限進(jìn)行優(yōu)化。當(dāng)你分配本地資源時(shí)(本地堆上的內(nèi)存,文件說明符等),往往很難實(shí)時(shí)的回收這些資源。同時(shí)你也需要在各個(gè)結(jié)構(gòu)中編譯你的代碼,而非依賴JIT。甚至可能需要針對(duì)相同的架構(gòu)來編譯出不同版本:針對(duì)ARM處理器的GI編譯的本地代碼,并不能充分利用Nexus One上的ARM,而針對(duì)Nexus One上ARM編譯的本地代碼不能在G1的ARM上運(yùn)行。

當(dāng)存在有你想部署到Android上的本地代碼庫時(shí),本地代碼顯得尤為有用,而非為了Java應(yīng)用程序的提速。

結(jié)語

最后:通常權(quán)衡的,先確定存在問題,再進(jìn)行優(yōu)化。確認(rèn)你知道當(dāng)前的性能,否則無法衡量你進(jìn)行嘗試所得到的提升。

這份文檔中的每個(gè)主張都有基準(zhǔn)測試作為支持。你可以在code.google.com的dalvik項(xiàng)目中找到基準(zhǔn)測試的代碼。

基準(zhǔn)測試是用Caliper Java微基準(zhǔn)測試框架構(gòu)建的。微基準(zhǔn)測試很難走對(duì),Caliper幫你完成了其中的困難工作。即使當(dāng)你察覺某些情況的測試結(jié)果并非你所想象的那樣(虛擬機(jī)總是在優(yōu)化你的代碼那)。我們強(qiáng)烈推薦你用Caliper來運(yùn)行你自己的微基準(zhǔn)測試。

同時(shí)你也會(huì)發(fā)現(xiàn)Traceview對(duì)分析很有用,但必須了解,他目前是不支持JIT的,這可能導(dǎo)致那些在JIT上可以勝出的代碼超時(shí)。特別重要的,當(dāng)根據(jù)Taceview的數(shù)據(jù)作出更改后,確保代碼在沒有Traceview時(shí),確實(shí)跑的快了.

【編輯推薦】

  1. Android 抱怨它還是擁抱它?
  2. 單挑蘋果 Android系“三劍客”尚需時(shí)日
  3. 谷歌開始限制手機(jī)廠商修改Android操作系統(tǒng)
  4. 谷歌出現(xiàn)Ice Cream涂鴉 暗示Android 3.1將至
  5. 縱覽Android發(fā)展歷程 成長但不成熟
責(zé)任編輯:佚名 來源: iteye
相關(guān)推薦

2010-02-04 09:41:03

Android應(yīng)用程序

2011-10-12 11:24:44

AndroidPC

2011-06-17 15:38:15

Cocoa蘋果

2009-07-10 17:24:07

Swing應(yīng)用程序

2010-03-04 10:11:17

Android手機(jī)系統(tǒng)

2011-08-08 13:35:50

Web應(yīng)用WANWeb應(yīng)用程序

2011-11-17 15:17:37

AdobeAIR調(diào)試性能

2013-07-12 15:40:47

Android技巧

2012-03-06 10:40:58

singleantJava

2009-01-08 19:11:39

服務(wù)器應(yīng)用程序SQL Server

2021-10-21 07:08:02

Windows 11操作系統(tǒng)微軟

2010-12-15 09:51:42

Android程序界面iPhone程序界面設(shè)

2020-12-28 14:40:47

云計(jì)算云應(yīng)用SaaS

2010-03-01 16:04:13

Linux Hadoo

2011-10-25 10:24:03

Windows Pho

2022-09-27 15:16:42

開發(fā)Android應(yīng)用程序

2012-04-25 22:56:10

Android

2010-03-03 16:45:46

Android應(yīng)用程序

2011-05-24 16:09:57

Androi

2010-01-25 13:29:53

Android本地應(yīng)用
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)