文章都為原創(chuàng),轉(zhuǎn)載請注明出處,未經(jīng)允許而盜用者追究法律責任。編寫者:李文棟 Android性能優(yōu)化根據(jù)Android的層次結(jié)構(gòu),性能優(yōu)化也是分層次進行的,本文會分別對Application、Framework、Native、Kernel各層做總結(jié),每層主要會從性能優(yōu)化的基本思想、優(yōu)化技巧、優(yōu)化工具幾個方面進行說明。 第一章Android應用性能優(yōu)化(概述)應用程序的性能問題是最明顯、最容易體現(xiàn)的一類,表現(xiàn)形式也五花八門,舉幾個例子:
除了表現(xiàn)形式復雜,原因也很復雜。以上的問題的原因可能不只一個,并且很多情況下并不是應用本身的問題,也有可能是系統(tǒng)其他層次有問題,只不過體現(xiàn)在應用層。所以說應用層總是首當其沖,開發(fā)人員在處理性能問題時,需要做的第一件事情就是判斷是否是應用自身引起的性能問題,然后再對癥下藥;但有些時候應用本身邏輯正常,明顯是系統(tǒng)的硬件配置不足引起,此時就要根據(jù)產(chǎn)品或項目需求,采取一些更加激進的方式優(yōu)化性能,以彌補硬件配置的不足。 以下從幾個不同的角度總結(jié)一下應用程序性能優(yōu)化的一些方法。 一、基本思想應用層的性能優(yōu)化通常可以從以下幾個方面考慮: 1. 了解編程語言的編譯原理,使用高效編碼方式從語法上提高程序性能; 2. 采用合理的數(shù)據(jù)結(jié)構(gòu)和算法提高程序性能,這往往是決定程序性能的關(guān)鍵; 3. 重視界面布局優(yōu)化; 4. 采用多線程、緩存數(shù)據(jù)、延遲加載、提前加載等手段,解決嚴重的性能瓶頸; 5. 合理配置虛擬機堆內(nèi)存使用上限和使用率,減少垃圾回收頻率; 6. 合理使用native代碼; 7. 合理配置數(shù)據(jù)庫緩存類型和優(yōu)化SQL語句加快讀取速度,使用事務加快寫入速度; 7. 使用工具分析性能問題,找出性能瓶頸; 當然肯定還有很多其他的性能優(yōu)化方法,此處僅列出一些經(jīng)常會用到的方法。限于篇幅,以下會對其中一部分內(nèi)容做介紹,希望能夠?qū)Υ蠹易鲂阅軆?yōu)化工作有所幫助。 二、編程技巧(一)Performance Tips (For Java) Google官網(wǎng)上有一些關(guān)于應用程序性能提升的技巧,之前公司內(nèi)也有很多總結(jié)提到過,在此簡單羅列一下,詳細內(nèi)容可以從官網(wǎng)獲取。 http://developer./training/articles/perf-tips.html 需要說明的是,文章列出的優(yōu)化技巧主要是一些微小的性能提升,決定程序整體性能的仍然取決于程序的業(yè)務邏輯設計、代碼的數(shù)據(jù)結(jié)構(gòu)和算法。研發(fā)人員需要將這些優(yōu)化技巧應用到平時的編碼過程中,積少成多,也會對性能有很大的影響。 寫出高效的代碼需要遵循兩條原則:
兩條原則分別針對CPU和內(nèi)存,完成必要操作的前提下盡可能的節(jié)省CPU和內(nèi)存資源,自然執(zhí)行效率要高。單純這樣說聽起來很虛,畢竟沒有一個統(tǒng)一的標準判斷什么是必要和不必要的,需要結(jié)合具體情況具體分析了。 1. 避免創(chuàng)建不必要的對象 創(chuàng)建太多的對象會造成性能低下,這誰都知道,可是為什么呢?首先分配內(nèi)存本身需要時間,其次虛擬機運行時堆內(nèi)存使用量是有上限的,當使用量到達一定程度時會觸發(fā)垃圾回收,垃圾回收會使得線程甚至是整個進程暫停運行。可想而知,如果有對象頻繁的創(chuàng)建和銷毀,或者內(nèi)存使用率很高,就會造成應用程序嚴重卡頓。 2.合理使用static成員 主要有三點需要掌握:
3. 避免內(nèi)部的Getters/Setters 面向?qū)ο笤O計中,字段訪問使用Getters/Setters通常是一個好的原則,但是在Android開發(fā)中限于硬件條件,除非字段需要被公開訪問,否則如果只是有限范圍內(nèi)的內(nèi)部訪問(例如包內(nèi)訪問)則不建議使用Getters/Setters。在開啟JIT時,直接訪問的速度比間接訪問要快7倍。 4. 使用for-each循環(huán) 優(yōu)先使用for-each循環(huán)通常情況下會獲得更高的效率;除了一種情況,即對ArrayList進行遍歷時,使用手動的計數(shù)循環(huán)效率要更高。 5. 使用package代替private以便私有內(nèi)部類高效訪問外部類成員 私有內(nèi)部類的方法訪問外部類的私有成員變量和方法,在語法上是正確的,但是虛擬機在運行時并不是直接訪問的,而是在編譯時會在外部類中自動生成一些包級別的靜態(tài)方法,執(zhí)行時內(nèi)部類會調(diào)用這些靜態(tài)方法來訪問外部類的私有成員。這樣的話就多了一層方法調(diào)用,性能有所損耗。 一種解決這個問題的方法就是將外部類的私有成員改為包級別的,這樣內(nèi)部類就可以直接訪問,當然前提是設計上可接受。 6. 避免使用浮點類型 經(jīng)驗之談,在Android設備中浮點型大概比整型數(shù)據(jù)處理速度慢兩倍,所以如果整型可以解決的問題就不要用浮點型。 另外,一些處理器有硬件乘法但是沒有除法,這種情況下除法和取模運算是用軟件實現(xiàn)的。為了提高效率,在寫運算式時可以考慮將一些除法操作直接改寫為乘法實現(xiàn),例如將“x / 2”改寫為“x * 0.5”。 7. 了解并使用庫函數(shù) Java標準庫和Android Framework中包含了大量高效且健壯的庫函數(shù),很多函數(shù)還采用了native實現(xiàn),通常情況下比我們用Java實現(xiàn)同樣功能的代碼的效率要高很多。所以善于使用系統(tǒng)庫函數(shù)可以節(jié)省開發(fā)時間,并且也不容易出錯。 (二)布局性能優(yōu)化 布局直接影響到界面的顯示時間。關(guān)于界面布局的性能優(yōu)化在技術(shù)上并沒有難點,個人認為最重要的是是否認識到布局優(yōu)化的重要性。起初我也會覺得布局本身不會是性能瓶頸,并且也很難優(yōu)化,好不容易寫了復雜的布局文件,或者原生代碼就是那樣,而且也用log查看了setContentView的時間,似乎沒什么問題,實在是不想去研究。但實際上布局問題沒有想象的那么簡單。 布局的性能優(yōu)化之所以重要,因為以下兩個方面: · 布局文件是一個xml文件,inflate布局文件其實就是解析xml,根據(jù)標簽信息創(chuàng)建相應的布局對象并做關(guān)聯(lián)。xml中的標簽和屬性設置越多,節(jié)點樹的深度越深,在解析時要執(zhí)行的判斷邏輯、函數(shù)的嵌套和遞歸就越多,所以時間消耗越多; · inflate操作只是布局影響的第一個環(huán)節(jié),一個界面要顯示出來,在requestLayout后還要執(zhí)行一系列的measure、layout、draw的操作,每一步的執(zhí)行時間都會受到布局本身的影響。而界面的最終顯示是所有這些操作完成后才實現(xiàn)的,所以如果布局質(zhì)量差,會增加每一步操作的時間成本,最終顯示時間就會比較長。 那么布局如何優(yōu)化?總結(jié)如下幾點: 1. 遵循一條規(guī)則:布局層次盡量少 也就是說,在達到同樣布局效果的前提下,xml文件中樹的深度盡量的潛。要做到這一點需要合理的使用布局控件:
2. 使用Lint分析布局 Lint是SDK中tools目錄下的工具,ADT中集成了Lint的可視化控制界面。用Lint掃描應用程序,它會從很多方面對應用進行分析,并提示那些可能有缺陷的地方,其中就包含與性能相關(guān)的內(nèi)容。你可以在Google官網(wǎng)上了解詳細信息。 http://developer./tools/debugging/improving-w-lint.html http://developer./tools/help/lint.html 3. 使用HierarchyViewer分析布局 HierarchyViewer(以下簡稱HV)也是SDK中tools目錄下的工具,ADT中也集成了HV的可視化控制界面??梢允褂肏V查看當前界面的布局,它能提供很多信息,其中有兩個可以幫助我們分析性能問題: · HV的樹視圖展現(xiàn)了視圖控件的相互關(guān)系,可以用來檢查是否有第1點中提到的情況。 · 樹視圖中可以顯示每個節(jié)點measure、layout、draw的時間,并且每一項用一個圓點表示其耗時是否正常,每個圓點分別用綠色、黃色、紅色表示耗時正常、警告、危險,這樣就可以很方便的找到有性能瓶頸了。如果樹視圖中沒有顯示這些時間,你可以點擊“Obtain layout times for tree rooted at selected node”按鈕刷新界面顯示。 http://developer./tools/debugging/debugging-ui.html 4. 使用ViewStub延遲加載視圖 ViewStub是一個沒有尺寸大小并且不會在布局中嵌套或渲染任何東西的輕量級的視圖。如果界面中有一部分視圖控件不需要立即顯示,則可以將其寫到一個單獨的layout文件中,用ViewStub標簽代替,當要真正顯示這部分內(nèi)容時再通過ViewStub將視圖加載進來。 http://developer./training/improving-layouts/loading-ondemand.html 三、工具使用遵循好的編碼習慣可以讓程序執(zhí)行更有效率,但是實際運行時仍然會遇到各種各樣的性能問題。幸好有很多強大的工具能幫助我們分析性能瓶頸,找到問題所在。以下介紹的工具想必大家已經(jīng)很熟悉了,網(wǎng)上有很多相關(guān)文章寫的都很不錯,在此不再贅述,僅對這些工具在使用時的一些關(guān)鍵點做一些說明。關(guān)于這些工具的詳細使用方法請見網(wǎng)上的一篇文章:http://blog.csdn.net/innost/article/details/9008691。 (一)Traceview 做性能優(yōu)化的最直接的方法,就是復現(xiàn)有性能問題的場景,并監(jiān)控此過程中程序的執(zhí)行流程,如果能夠方便的分析程序中函數(shù)的調(diào)用關(guān)系和執(zhí)行時間,自然也就很容易找出性能瓶頸了。 Traceview就是用來分析函數(shù)調(diào)用過程的工具,利用它可以方便的分析性能問題。它的使用方式需要以下幾步:
Traceview的界面很直觀,但是在分析過程中需要特別注意以下幾點: 1. Profile Panel中的各列的含義: · Incl – 指函數(shù)本身和內(nèi)部嵌套的其他函數(shù)的執(zhí)行時間; · Excl - 指函數(shù)本身,不包含內(nèi)部嵌套的其他函數(shù)的執(zhí)行時間; · Cpu Time – 指函數(shù)執(zhí)行時所占用的CPU時間片的總和,不包含等待調(diào)度的時間; · Real Time – 指函數(shù)執(zhí)行過程的真實時間,包含等待調(diào)度的時間; · Cpu Time/Call – 指函數(shù)平均每次調(diào)用的CPU時間; · Real Time/Call – 指函數(shù)平均每次調(diào)用的真實時間; · Calls+Recur Calls/Total – 指函數(shù)調(diào)用的總次數(shù)+遞歸調(diào)用次數(shù)百分比; · % - 帶有%的列是指函數(shù)的執(zhí)行時間占總采樣時間的百分比; 2. 如何分析性能瓶頸 首先通常需要關(guān)心的是CPU時間,可以找出程序自身的問題,真實時間會受到系統(tǒng)其他因素的影響。然后可以從四個方面進行分析: 1)分析有哪些函數(shù)單次執(zhí)行時間長 可以點擊“Cpu Time/Call”一列,按照降序排列,并找出那些執(zhí)行時間相對較長同時也是我們關(guān)心的函數(shù),然后再查看其函數(shù)內(nèi)部的詳細執(zhí)行過程; 2)分析有哪些函數(shù)調(diào)用次數(shù)過多 可以點擊“Calls+RecurCalls/Total”一列,按照降序排列,并找出哪些執(zhí)行次數(shù)相對較多同時也是我們關(guān)心的函數(shù),然后再查看其函數(shù)內(nèi)部的詳細執(zhí)行過程; 3)分析有哪些函數(shù)總執(zhí)行時間長 有些函數(shù)的單次執(zhí)行時間不是特別長,總調(diào)用次數(shù)也不是特別多,但是二者相乘得出的總的執(zhí)行時間較長,可以點擊“Incl Cpu Time”,按照降序排列,找出這些函數(shù); 4)有時我們很明確需要查看一些特定類的特定方法,可以在頁面最下方的搜索條中搜索,不過好像只支持全小寫輸入。 3. 提示一點:利用API或工具采樣trace信息時,會禁用JIT功能,同時因為采樣本身也需要占用系統(tǒng)資源,所以用Traceview查看函數(shù)的執(zhí)行時間都要比正常運行時慢不少,我們只要關(guān)心相對的時間消耗即可。 (二)dmtracedump trace文件除了可以用TraceView分析外,還可以利用另外一個工具dmtracedump,它的功能也很強大。如果你覺得在Traceview中查找類和函數(shù)很痛苦,不妨試試這個工具。 dmtracedump是SDK的tools目錄下的可執(zhí)行文件,你可以查看它的幫助信息,并執(zhí)行類似如下的命令: dmtracedump -h -g tracemap.png path-to-your-trace-file > path-to-a-html-file.html 然后就可以得到兩樣東西,一個是各函數(shù)調(diào)用的樹狀圖,可以一目了然的查看函數(shù)關(guān)系;另一個是可操作的html的文件,用瀏覽器打開就可以方便的查找你關(guān)心的類或函數(shù)。 (三)systrace Systrace是從4.1引入的一個強大的性能分析工具,依賴于Kernel的ftrace功能,可以對系統(tǒng)中很多重要模塊,特別是圖形顯示模塊做性能分析。它功能包括跟蹤系統(tǒng)的I/O操作、內(nèi)核工作隊列、CPU負載以及Android各個子系統(tǒng)的運行狀況等。 Systrace的使用方法也是需要先通過Android提供的API或者DDMS開啟跟蹤監(jiān)控模式,然后運行程序生成日志文件,最后分析日志文件即可。Systrace輸出的是一個html文件,直接用瀏覽器查看即可。如果你使用最新版本的ADT,可以很方便的通過界面操作,不用再用命令了。更詳細的內(nèi)容可以在網(wǎng)上搜索。 四、關(guān)于性能優(yōu)化的思考性能優(yōu)化是一個很大的話題,除了討論如何優(yōu)化外,還有一個更重要的就是是否需要優(yōu)化。早在幾十年前,就有很多關(guān)于性能優(yōu)化的討論,然后得出一個深刻的真理:優(yōu)化更容易帶來傷害,而不是好處,特別是不成熟的優(yōu)化。在優(yōu)化過程中,你產(chǎn)生的軟件可能既不快速,也不正確,而且還不容易被修正。 不要因為性能而犧牲合理的結(jié)構(gòu)。努力編寫好的程序而不是快的程序。 但是,這并不意味著,在完成程序之前你就可以忽略性能問題。實現(xiàn)上的問題可以通過后期的優(yōu)化而被改正,但遍布全局并且限制性能的結(jié)構(gòu)缺陷幾乎是不可能被改正的,除非重新編寫程序。在系統(tǒng)完成之后再改變你的設計的某個基本方面,會導致你的系統(tǒng)結(jié)構(gòu)病態(tài),從而難以維護和改進。因此你應該在設計過程中考慮性能問題。
努力避免那些限制性能的設計??紤]你的代碼設計的性能后果。為獲得好的性能而對代碼進行曲改,是一個非常不好的想法。在每次做優(yōu)化之前和之后,需要對性能進行測量。 |
|
來自: 老匹夫 > 《performance》