如何編寫高效android代碼

來源:互聯網
上載者:User

標籤:

基於Android相關裝置作為嵌入式裝置範疇,在書寫App應用的時候要格外關注效率,並且受電池電量的限制。這就導致嵌入式裝置有諸多考慮、有限處理能力,因此就要求我們盡量去寫高效的代碼。本文討論了很多能讓開發人員使他們的程式運行更有效方法,遵照這些方法,你可以使你的程式發揮最大的效力。


引言
對於佔用資源的系統,有兩條基本原則:

不要做不必要的事

不要分配不必要的記憶體


所有下面的內容都遵照這兩個原則。

1、避免建立對象

世界上沒有免費的對象。雖然GC為每個線程都建立了臨時對象池,可以使建立對象的代價變得小一些,但是分配記憶體永遠都比不分配記憶體的代價大。
如果你在使用者介面迴圈中指派至記憶體,就會引發周期性的記憶體回收,使用者就會覺得介面像打嗝一樣一頓一頓的。
所以,除非必要,應盡量避免儘力對象的執行個體。下面的例子將協助你理解這條原則:


 當你從使用者輸入的資料中截取一段字串時,盡量使用substring函數取得未經處理資料的一個子串,而不是為子串另外建立一份拷貝。這樣你就有一個新的String對象,它與未經處理資料共用一個char數組。
如果你有一個函數返回一個String對象,而你確切的知道這個字串會被附加到一個StringBuffer,那麼,請改變這個函數的參數和實現方式,直接把結果附加到StringBuffer中,而不要再建立一個短命的臨時對象。
一個更極端的例子是,把多維陣列分成多個一維數組:

int數組比Integer數組好,這也概括了一個基本事實,兩個平行的int數組比(int,int)對象數組效能要好很多。同理,這試用於所有基本類型的組合。
如果你想用一種容器儲存(Foo,Bar)元組,嘗試使用兩個單獨的Foo[]數組和Bar[]數組,一定比(Foo,Bar)數組效率更高。(也有例外的情況,就是當你建立一個API,讓別人調用它的時候。這時候你要注重對API借口的設計而犧牲一點兒速度。當然在API的內部,你仍要儘可能的提高代碼的效率)
總體來說,就是避免建立短命的臨時對象。減少對象的建立就能減少垃圾收集,進而減少對使用者體驗的影響。


2、使用本地方法

當你在處理字串的時候,不要吝惜使用String.indexOf(), String.lastIndexOf()等特殊實現的方法。這些方法都是使用C/C++實現的,比起Java迴圈快10到100倍。
但也並非要完全使用本地方法,調用本地方法的代價要高於調用解釋方法。所以如果可以避免,就不應使用本地方法去做一些並不複雜的運算。


3、選擇虛類而不是介面

假設你有一個HashMap對象,你可以將它聲明為HashMap或者Map:


Map myMap1 = new HashMap();

HashMap myMap2 = new HashMap();哪個更好呢?


按照傳統的觀點Map會更好些,因為這樣你可以改變他的具體實作類別,只要這個類繼承自Map介面。傳統的觀點對於傳統的程式是正確的,但是它並不適合嵌入式系統。調用一個介面的引用會比調用實體類的引用多花費一倍的時間。如果HashMap完全適合你的程式,那麼使用Map就沒有什麼價值。如果有些地方你不能確定,先避免使用Map,剩下的交給IDE提供的重構功能好了。(當然公用API是一個例外:一個好的API常常會犧牲一些效能)


 


4、用靜態方法比虛方法好


如果你不需要訪問一個對象的成員變數,那麼請把方法聲明成static。虛方法執行的更快,因為它可以被直接調用而不需要一個虛函數表。另外你也可以通過聲明體現出這個函數的調用不會改變對象的狀態。


 
5、不用getter和setter


在很多本地語言如C++中,都會使用getter(比如:i = getCount())來避免直接存取成員變數(i = mCount)。在C++中這是一個非常好的習慣,因為編譯器能夠內聯訪問,如果你需要約束或調試變數,你可以在任何時候添加代碼。在Android上,這就不是個好主意了。虛方法的開銷比直接存取成員變數大得多。在通用的介面定義中,可以依照OO的方式定義getters和setters,但是在一般的類中,你應該直接存取變數。


 

6、將成員變數緩衝到本地


訪問成員變數比訪問本地變數慢得多,下面一段代碼:

  for (int i = 0; i < this.mCount; i++)


    dumpItem(this.mItems[i]);你應該寫成:

  int count = this.mCount;


  Item[] items = this.mItems;


  for (int i = 0; i < count; i++)


    dumpItems(items[i]);(顯示的使用”this”是為了表明這些是成員變數)


 
另一個相似的原則是:永遠不要在for的第二個條件中調用任何方法。如下面方法所示,在每次迴圈的時候都會調用getCount()方法,這樣做比你在一個int先把結果儲存起來開銷大很多。

  for (int i = 0; i < this.getCount();i++)

    dumpItems(this.getItem(i));同樣如果你要多次訪問一個變數,也最好先為它建立一個本地變數,例如:


protected void drawHorizontalScrollBar(Canvas canvas, int width, int height) {

if (isHorizontalScrollBarEnabled()) {

int size = mScrollBar.getSize(false);

if (size <= 0) {

size = mScrollBarSize;
}


mScrollBar.setBounds(0, height – size, width, height);


mScrollBar.setParams(

computeHorizontalScrollRange(),

computeHorizontalScrollOffset(),

computeHorizontalScrollExtent(), false);

mScrollBar.draw(canvas);
}
}


 這裡有4次訪問成員變數mScrollBar,如果將它緩衝到本地,4次成員變數訪問就會變成4次效率更高的棧變數訪問。

順便說明一下,就是方法的參數與本地變數的效能是相同的。


 


7、使用常量


讓我們來看看這兩段在類前面的聲明:

static int intVal = 42;

static String strVal = “Hello, world!”;


編譯器會產生一個叫做<clinit>的初始化類的方法,當類第一次被使用的時候這個方法會被執行。方法會將42賦給intVal,然後把一個指向類中常量表的引用賦給strVal。當以後要用到這些值的時候,會在成員變數表中尋找到他們。


我們可以做一些改進,使用“final”關鍵字:

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

現在,類不再需要<clinit>方法,因為在成員變數初始化的時候,會將常量直接儲存到類檔案中。用到intVal的代碼被直接替換成42,而使用strVal的會指向一個字串常量,而不是使用成員變數。


將一個方法或類聲明為”final”不會帶來效能的提升,但是會協助編譯器最佳化代碼。舉例說,如果編譯器知道一個”getter”方法不會被重載,那麼編譯器會對其採用內聯調用。

你也可以將本地變數聲明為”final”,同樣,這也不會帶來效能的提升。使用”final”只能使本地變數看起來更清晰些(但是也有些時候這是必須的,比如在使用匿名內部類的時候)。


8、謹慎使用foreach

9、避免使用枚舉

如何編寫高效android代碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.