【Android開發】Android效能最佳化

來源:互聯網
上載者:User

標籤:android   blog   http   io   ar   os   使用   sp   java   

Android效能最佳化

    根據Android的階層,效能最佳化也是分層次進行的,本文會分別對Application、Framework、Native、Kernel各層做總結,每層主要會從效能最佳化的基本思想、最佳化技巧、最佳化工具幾個方面進行說明。

第一章Android應用效能最佳化(概述)

    應用程式的效能問題是最明顯、最容易體現的一類,表現形式也五花八門,舉幾個例子:

  • 應用程式第一次啟動速度慢,或者進入某一介面速度慢;
  • 啟動某一有動畫效果的介面,動畫執行過程不流暢,或者動畫執行前卡頓時間長;
  • ListView列表滑動過程中卡頓,不流暢;
  • 應用程式自訂的某特定介面執行速度慢,例如Launcher應用案頭左右滑動效果不平滑;
  • 響應某一使用者事件時間長度時間無響應(ANR);
  • 操作資料庫時,執行大量資料的增刪改查操作,執行速度慢;
  • 應用長時間運行後,隨機出現卡頓現象;

       除了表現形式複雜,原因也很複雜。以上的問題的原因可能不只一個,並且很多情況下並不是應用本身的問題,也有可能是系統其他層次有問題,只不過體現在應用程式層。所以說應用程式層總是首當其衝,開發人員在處理效能問題時,需要做的第一件事情就是判斷是否是應用自身引起的效能問題,然後再對症下藥;但有些時候應用本身邏輯正常,明顯是系統的硬體設定不足引起,此時就要根據產品或項目需求,採取一些更加激進的方式最佳化效能,以彌補硬體設定的不足。

        以下從幾個不同的角度總結一下應用程式效能最佳化的一些方法。

一、基本思想

        應用程式層的效能最佳化通常可以從以下幾個方面考慮:

1. 瞭解程式設計語言的編譯原理,使用高效編碼方式從文法上提高程式效能;

2. 採用合理的資料結構和演算法提高程式效能,這往往是決定程式效能的關鍵;

3. 重視介面布局最佳化;

4. 採用多線程、快取資料、消極式載入、提前載入等手段,解決嚴重的效能瓶頸;

5. 合理配置虛擬機器堆記憶體使用量上限和使用率,減少記憶體回收頻率;

6. 合理使用native代碼;

7. 合理設定資料庫緩衝類型和最佳化SQL語句加快讀取速度,使用事務加快寫入速度;

7. 使用工具分析效能問題,找出效能瓶頸;

       當然肯定還有很多其他的效能最佳化方法,此處僅列出一些經常會用到的方法。限於篇幅,以下會對其中一部分內容做介紹,希望能夠對大家做效能最佳化工作有所協助。

二、編程技巧

(一)Performance Tips (For Java)

       Google官網上有一些關於應用程式效能提升的技巧,之前公司內也有很多總結提到過,在此簡單羅列一下,詳細內容可以從官網擷取。

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

需要說明的是,文章列出的最佳化技巧主要是一些微小的效能提升,決定程式整體效能的仍然取決於程式的商務邏輯設計、代碼的資料結構和演算法。研發人員需要將這些最佳化技巧應用到平時的編碼過程中,積少成多,也會對效能有很大的影響。

寫出高效的代碼需要遵循兩條原則:

  • 不執行不必要的操作;
  • 不分配不必要的記憶體;

       兩條原則分別針對CPU和記憶體,完成必要操作的前提下儘可能的節省CPU和記憶體資源,自然執行效率要高。單純這樣說聽起來很虛,畢竟沒有一個統一的標準判斷什麼是必要和不必要的,需要結合具體情況具體分析了。

     1. 避免建立不必要的對象

       建立太多的對象會造成效能低下,這誰都知道,可是為什麼呢?首先分配記憶體本身需要時間,其次虛擬機器運行時堆記憶體使用量量是有上限的,當使用量到達一定程度時會觸發記憶體回收,記憶體回收會使得線程甚至是整個進程暫停運行。可想而知,如果有對象頻繁的建立和銷毀,或者記憶體使用量率很高,就會造成應用程式嚴重卡頓。

     2.合理使用static成員

     主要有三點需要掌握:

  • 如果一個方法不需要操作運行時的動態變數和方法,那麼可以將方法設定為static的。
  • 常量欄位要聲明為“static final”,因為這樣常量會被存放在dex檔案的靜態欄位初始化器中被直接存取,否則在運行時需要通過編譯時間自動產生的一些函數來初始化。此規則只對基本類型和String類型有效。
  • 不要將視圖控制項聲明為static,因為View對象會引用Activity對象,當Activity退出時其對象本身無法被銷毀,會造成記憶體溢出。

       3. 避免內部的Getters/Setters

       物件導向設計中,欄位訪問使用Getters/Setters通常是一個好的原則,但是在Android開發中限於硬體條件,除非欄位需要被公開訪問,否則如果只是有限範圍內的內部訪問(例如包內訪問)則不建議使用Getters/Setters。在開啟JIT時,直接存取的速度比間接訪問要快7倍。

       4. 使用for-each迴圈

       優先使用for-each迴圈通常情況下會獲得更高的效率;除了一種情況,即對ArrayList進行遍曆時,使用手動的計數迴圈效率要更高。

       5. 使用package代替private以便私人內部類高效訪問外部類成員

       私人內部類的方法訪問外部類的私人成員變數和方法,在文法上是正確的,但是虛擬機器在運行時並不是直接存取的,而是在編譯時間會在外部類中自動產生一些包層級的靜態方法,執行時內部類會調用這些靜態方法來訪問外部類的私人成員。這樣的話就多了一層方法調用,效能有所損耗。

一種解決這個問題的方法就是將外部類的私人成員改為包層級的,這樣內部類就可以直接存取,當然前提是設計上可接受。

       6. 避免使用浮點類型

       經驗之談,在Android裝置中浮點型大概比整型資料處理速度慢兩倍,所以如果整型可以解決的問題就不要用浮點型。

       另外,一些處理器有硬體乘法但是沒有除法,這種情況下除法和模數運算是用軟體實現的。為了提高效率,在寫運算式時可以考慮將一些除法操作直接改寫為乘法實現,例如將“x / 2”改寫為“x * 0.5”。

       7. 瞭解並使用庫函數

Java標準庫和Android Framework中包含了大量高效且健壯的庫函數,很多函數還採用了native實現,通常情況下比我們用Java實現同樣功能的代碼的效率要高很多。所以善於使用系統庫函數可以節省開發時間,並且也不容易出錯。

(二)布局效能最佳化

       布局直接影響到介面的顯示時間。關於介面布局的效能最佳化在技術上並沒有痛點,個人認為最重要的是是否認識到布局最佳化的重要性。起初我也會覺得布局本身不會是效能瓶頸,並且也很難最佳化,好不容易寫了複雜的布局檔案,或者原生代碼就是那樣,而且也用log查看了setContentView的時間,似乎沒什麼問題,實在是不想去研究。但實際上布局問題沒有想象的那麼簡單。

布局的效能最佳化之所以重要,因為以下兩個方面:

·           布局檔案是一個xml檔案,inflate布局檔案其實就是解析xml,根據標籤資訊建立相應的布局對象並做關聯。xml中的標籤和屬性設定越多,節點樹的深度越深,在解析時要執行的判斷邏輯、函數的嵌套和遞迴就越多,所以時間消耗越多;

·        inflate操作只是布局影響的第一個環節,一個介面要顯示出來,在requestLayout後還要執行一系列的measure、layout、draw的操作,每一步的執行時間都會受到布局本身的影響。而介面的最終顯示是所有這些操作完成後才實現的,所以如果布局品質差,會增加每一步操作的時間成本,最終顯示時間就會比較長。

       那麼布局如何最佳化?總結如下幾點:

     1. 遵循一條規則:布局層次盡量少

也就是說,在達到同樣布局效果的前提下,xml檔案中樹的深度盡量的潛。要做到這一點需要合理的使用布局控制項:

  • 典型的情況是你可以使用RelativeLayout來代替LinearLayout實現相同的布局效果;
  • 還有一種是如果布局樹的A節點只有一個子節點B,而B只有一個子節點C,那麼B通常是可以去掉的;
  • 合理的使用<merge>標籤,如果布局X可以被include到Y中,那麼需要考慮X的根節點是否可以設定為<merge>,這樣在解析時會將<merge>的子節點添加到Y中,而<merge>本身不會添加。

     2. 使用Lint分析布局

       Lint是SDK中tools目錄下的工具,ADT中整合了Lint的可視化控制介面。用Lint掃描應用程式,它會從很多方面對應用進行分析,並提示那些可能有缺陷的地方,其中就包含與效能相關的內容。你可以在Google官網上瞭解詳細資料。

http://developer.android.com/tools/debugging/improving-w-lint.html

http://developer.android.com/tools/help/lint.html

     3. 使用HierarchyViewer分析布局

       HierarchyViewer(以下簡稱HV)也是SDK中tools目錄下的工具,ADT中也整合了HV的可視化控制介面。可以使用HV查看當前介面的布局,它能提供很多資訊,其中有兩個可以協助我們分析效能問題:

·           HV的樹視圖展現了視圖控制項的相互關係,可以用來檢查是否有第1點中提到的情況。

·          樹視圖中可以顯示每個節點measure、layout、draw的時間,並且每一項用一個圓點表示其耗時是否正常,每個圓點分別用綠色、黃色、紅色表示耗時正常、警告、危險,這樣就可以很方便的找到有效能瓶頸了。如果樹視圖中沒有顯示這些時間,你可以點擊“Obtain layout times for tree rooted at selected node”按鈕重新整理介面顯示。

http://developer.android.com/tools/debugging/debugging-ui.html

       4. 使用ViewStub消極式載入視圖

       ViewStub是一個沒有尺寸大小並且不會在布局中嵌套或渲染任何東西的輕量級的視圖。如果介面中有一部分視圖控制項不需要立即顯示,則可以將其寫到一個單獨的layout檔案中,用ViewStub標籤代替,當要真正顯示這部分內容時再通過ViewStub將視圖載入進來。

http://developer.android.com/training/improving-layouts/loading-ondemand.html

三、工具使用

     遵循好的編碼習慣可以讓程式執行更有效率,但是實際運行時仍然會遇到各種各樣的效能問題。幸好有很多強大的工具能協助我們分析效能瓶頸,找到問題所在。以下介紹的工具想必大家已經很熟悉了,網上有很多相關文章寫的都很不錯,在此不再贅述,僅對這些工具在使用時的一些關鍵點做一些說明。關於這些工具的詳細使用方法請見網上的一篇文章:http://blog.csdn.net/innost/article/details/9008691。

(一)Traceview

       做效能最佳化的最直接的方法,就是複現有效能問題的情境,並監控此過程中程式的執行流程,如果能夠方便的剖析器中函數的調用關係和執行時間,自然也就很容易找出效能瓶頸了。

Traceview就是用來分析函數調用過程的工具,利用它可以方便的分析效能問題。它的使用方式需要以下幾步:

  • 使用Android的Debug API,或者DDMS監控程式運行過程;
  • 複現有效能問題的情境,用第1步的方法擷取程式過程中的函數調用記錄檔,即trace檔案;
  • 使用Traceview匯入trace檔案即可;

       Traceview的介面很直觀,但是在分析過程中需要特別注意以下幾點:

1. Profile Panel中的各列的含義

·         Incl – 指函數本身和內部嵌套的其他函數的執行時間;

·         Excl -  指函數本身,不包含內部嵌套的其他函數的執行時間;

·         Cpu Time – 指函數執行時所佔用的CPU時間片的總和,不包含等待調度的時間;

·         Real Time – 指函數執行過程的真即時間,包含等待調度的時間;

·         Cpu Time/Call – 指函數平均每次調用的CPU時間;

·         Real Time/Call – 指函數平均每次調用的真即時間;

·         Calls+Recur Calls/Total – 指函數調用的總次數+遞迴調用次數百分比;

·         % - 帶有%的列是指函數的執行時間佔總採樣時間的百分比;

     2. 如何分析效能瓶頸

       首先通常需要關心的是CPU時間,可以找出程式自身的問題,真即時間會受到系統其他因素的影響。然後可以從四個方面進行分析:

1)分析有哪些函數單次執行時間長

       可以點擊“Cpu Time/Call”一列,按照降序排列,並找出那些執行時間相對較長同時也是我們關心的函數,然後再查看其函數內部的詳細執行過程;

2)分析有哪些函數調用次數過多

       可以點擊“Calls+RecurCalls/Total”一列,按照降序排列,並找出哪些執行次數相對較多同時也是我們關心的函數,然後再查看其函數內部的詳細執行過程;

3)分析有哪些函數總執行時間長

       有些函數的單次執行時間不是特別長,總調用次數也不是特別多,但是二者相乘得出的總的執行時間較長,可以點擊“Incl Cpu Time”,按照降序排列,找出這些函數;

4)有時我們很明確需要查看一些特定類的特定方法,可以在頁面最下方的搜尋條中搜尋,不過好像只支援全小寫輸入。

       3. 提示一點:利用API或工具採樣trace資訊時,會禁用JIT功能,同時因為採樣本身也需要佔用系統資源,所以用Traceview查看函數的執行時間都要比正常運行時慢不少,我們只要關心相對的時間消耗即可。

(二)dmtracedump

     trace檔案除了可以用TraceView分析外,還可以利用另外一個工具dmtracedump,它的功能也很強大。如果你覺得在Traceview中尋找類和函數很痛苦,不妨試試這個工具。

dmtracedump是SDK的tools目錄下的可執行檔,你可以查看它的協助資訊,並執行類似如下的命令:

dmtracedump -h -g tracemap.png path-to-your-trace-file > path-to-a-html-file.html

然後就可以得到兩樣東西,一個是各函數調用的樹狀圖,可以一目瞭然的查看函數關係;另一個是可操作的html的檔案,用瀏覽器開啟就可以方便的尋找你關心的類或函數。

(三)systrace

       Systrace是從4.1引入的一個強大的效能分析工具,依賴於Kernel的ftrace功能,可以對系統中很多重要模組,特別是圖形顯示模組做效能分析。它功能包括跟蹤系統的I/O操作、核心工作隊列、CPU負載以及Android各個子系統的健全狀態等。

       Systrace的使用方法也是需要先通過Android提供的API或者DDMS開啟跟蹤監控模式,然後運行程式組建記錄檔檔案,最後分析記錄檔即可。Systrace輸出的是一個html檔案,直接用瀏覽器查看即可。如果你使用最新版本的ADT,可以很方便的通過介面操作,不用再用命令了。更詳細的內容可以在網上搜尋。

四、關於效能最佳化的思考

       效能最佳化是一個很大的話題,除了討論如何最佳化外,還有一個更重要的就是是否需要最佳化。早在幾十年前,就有很多關於效能最佳化的討論,然後得出一個深刻的真理:最佳化更容易帶來傷害,而不是好處,特別是不成熟的最佳化。在最佳化過程中,你產生的軟體可能既不快速,也不正確,而且還不容易被修正。

不要因為效能而犧牲合理的結構。努力編寫好的程式而不是快的程式。

       但是,這並不意味著,在完成程式之前你就可以忽略效能問題。實現上的問題可以通過後期的最佳化而被改正,但遍布全域並且限制效能的結構缺陷幾乎是不可能被改正的,除非重新編寫程式。在系統完成之後再改變你的設計的某個基本方面,會導致你的系統結構病態,從而難以維護和改進。因此你應該在設計過程中考慮效能問題。

 

       努力避免那些限制效能的設計。考慮你的代碼設計的效能後果。為獲得好的效能而對代碼進行曲改,是一個非常不好的想法。在每次做最佳化之前和之後,需要對效能進行測量。

 

轉自:http://rayleeya.iteye.com/blog/1961005

【Android開發】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.