Android應用性能優化實踐

jopen 9年前發布 | 11K 次閱讀 Android Android開發 移動開發

本文出自: UDI COHEN ,作者: Udi Cohen譯文出自: 開發技術前線 ,譯者: Zijian Wang


幾周前,我在Droidcon NYC上有過一次關于Android性能優化的演講。 我在這個演講中花費了大量的時間,因為我想通過真實的例子展現性能問題,以及我是通過什么樣的工具去發掘這些問題的。因為時間原因,在演講中我不得不舍棄一半的內容。在這篇文章中,我會總結在演講中我所討論的所有內容,并且給出實例。( 點擊鏈接 觀看演講視頻,需自備梯子)

現在,我們來逐一討論我在演講中提及的一些重點內容,希望我的闡述足夠的清晰。首先,在我進行性能優化的時候我遵循如下原則:

原則

每當我遇到性能問題,或者嘗試發現性能問題的時候,我會遵循如下原則:

  • 堅持性能測試——不要用你的眼睛去優化性能。也許在你盯著同一個動畫看了幾次之后,你會開始相信它運行得越來越流暢了。數據不會說謊。在優化你的代碼前后,使用我們將要介紹的一系列工具,去多次地測試你的App到底性能幾何。
  • 使用低端設備——如果你想要你想暴露你應用的性能問題,低端設備往往會更加的容易。性能強大的設備往往不會太在意你應用上面的一些優化問題,且不是所有用戶都在使用這些旗艦設備。
  • 權衡——性能的優化始終圍繞著權衡這兩個字。你在某一個點上的優化可能會造成另一點上出現問題。在很多情況下,你會花大量的時 間尋找并解決這些問題,但造成這些問題的原因也可能使因為例如bitmaps的質量,或是你沒有使用正確的數據結構去存儲你的數據。所以你要時刻準備好作 出一定的犧牲。

Systrace

Systrace是一個非常好但卻有可能被你忽視的工具,這是因為開發者們往往不確定Systrace能夠為他們提供什么樣的信息。

Systrace會展示一個運行在手機上程序狀況的概覽。這個工具提醒了我們手機其實是一個可以在同一時間完成很多工作的電腦。在最近的一次SDK更新中,這個工具在數據分析能力上得到了提升,用以幫助我們尋找性能問題之所在。

下面讓我們來看看Systrace長什么樣子:

Android應用性能優化實踐

你可以通過Android Device Monitor Tool或命令行來生成Systrace文件,想了解更多 猛戳此處

在視頻中,我向大家介紹了Systrace中不同區域的功能。當然最有趣的還是Alerts和Frames兩欄,它們展示了通過手機來的數據而生成出來的可視化分析結果。讓我們來選擇最上方的alerts瞧瞧:

Android應用性能優化實踐

這個警告指出了,有一個View#draw()方法執行了比較長的時間。我們可以在下面看到問題的描述,鏈接,甚至是相關的視頻。下面我們看 Frames這一行,可以看到這里展示了被繪制出來的每一幀,并且用綠、黃、紅三顏色來區分它們在繪制時的性能。我們選一個紅色幀來瞅瞅:

Android應用性能優化實踐

在最下方,我們看到了與這一幀所相關的一些警告。在這三個警告中,有一個是我們上面所提到的(View#draw())。接下來我們在這一幀處放大并在下方展開“Inflation during ListView recycling”這條警告:

Android應用性能優化實踐

我們可以看到警告部分的總耗時,32毫秒,遠高于了我們對保障60fps所需的16毫秒繪制時間。同時還有更多的ListView每個條目的繪制 時間,大約是6毫秒每個條目,總共五個。而Description描述項中的內容會幫助我們理解問題,甚至提供問題的解決方案。回到我們上一張圖片,我們 可以在“inflate”這一個塊區處放大,并且觀察到底是哪些View在被填充過程中耗時比較嚴重。

下面是另外一個渲染過慢的實例:

Android應用性能優化實踐

在選擇了某一幀之后,我們可以按“m”鍵來高亮這一幀,并且在上方看到了這一部分的耗時,如圖,我們看到了這一陣的繪制總共耗時超過19毫秒。而當我們展開這一幀唯一的一個警告時,我們發現了“Scheduling delay”這條錯誤。

Scheduling delay(調度延遲)的意思就是一個線程在處理一塊運算的時候,在很長一段時間都沒有被分配到CPU上面做運算,從而導致這個線程在很長一段時間都沒有完成工作。我們選擇這一幀中最長的一塊,從而得到更加詳細的信息:

Android應用性能優化實踐

在紅框區域內,我們看到了“Wall duration”,他代表著這一區塊的開始到結束的耗時。之所以叫作“Wall duration”,是因為他就像是墻上的一個時鐘,從線程的一開始就為你計時。

但是,CPU Duration一項中顯示了實際CPU在處理這一區塊所消耗的時間。

很顯然,兩個時間的差距還是非常大的。整個區塊耗時18毫秒,而在這之中CPU只消耗了4毫秒的時間去運算。這就有點奇怪了,所以我們應該看一下在這整個過程之中,CPU去干嗎了。

Android應用性能優化實踐

可以看到,所有四個線程都非常的繁忙。

選擇其中的一個線程會告訴我們是哪個程序在占用他,在這里是一個包名為com.udinic.keepbusyapp的程序。在這里,由于另外一個程序占用CPU,導致了我們的程序未能獲得足夠的CPU資源。

但是這種情況其實是暫時的,因為被其他后臺應用占用CPU的情況并不多見(- -),但仍有其他應用的線程或是主線程占用CPU。而Traceview也只能為我們提供一個概覽,他的深度是有限的。所以要找到我們app中到底是什么 讓我們的CPU繁忙,我們還要借助另一個工具——Traceview。

Traceview

Traceview是一個性能測試工具,展示了所有方法的運行時間。下面讓我們來瞅瞅它是啥樣的:

Android應用性能優化實踐

這個工具可以從Android Device Monitor中打開也可以通過代碼打開。更多的消息信息請看 這里

下面讓我們來看看每一列的含義:

  • Name——方法名,以及他們在上面圖表中所對應的顏色。
  • Inclusive CPU Time——CPU在處理這個方法以及所有子方法(如被他調用的所有方法)的總耗時。
  • Exclusive CPU Time——CPU在處理這一個單獨方法的總耗時。
  • Inclusive/Exlusive Real Time——從方法的開始執行到執行結束的總耗時,和Systrace中的“Wall duration”類似。
  • Calls+Recursion——這個方法被調用的次數,以及被遞歸調用的次數。
  • CPU/Real time per Call——在處理這個方法時的CPU耗時的平均值以及實際耗時的平均值。另外的列展示了這個方法所有調用的累計耗時。

我打開一個滑動不太順滑的應用。開啟記錄,滑動一點后停止記錄。展開getView()方法,如下圖:

Android應用性能優化實踐

這個方法被調用了12次,每次CPU會消耗3毫秒左右,但是每次調用的總耗時卻高達162毫秒!絕對有問題啊!

而看看這個方法的children,我們可以看到這其中的每個方法在耗時方面是如何分布的。Thread.join()方法戰局了98%的 inclusive real time。這個方法在等待另一個線程結束的時候被調用。在Children中另外一個方法就是Tread.start()方法,而之所以整個方法耗時很 長,我猜測是因為在getView()方法中啟動了線程并且在等待它的結束。

但是這個線程在哪兒?

我們在getView()方法中并不能看到這個線程做了什么,因為這段邏輯不在getView()方法之中。于是我找到了Thread.run()方法,就是在線程被創建出來時候所運行的方法。而跟隨這個方法一路向下,我找到了問題的元兇。

Android應用性能優化實踐

我發現了BgService.doWork()方法的每次調用花費了將近14毫秒,并且有四十個這東西!而且getView()中還有可能調用多 次這個方法,這就解釋了為什么getView()方法執行時間如此之長。這個方法讓CPU長時間的保持在了繁忙狀態。而看看Exclusive CPU time,我們可以看到他占據了80%的CPU時間!此外,根據Exclusive CPU time排序,可以幫我們更好的定位那些耗時很長的方法,而他們很有可能就是造成性能問題的罪魁禍首。

關注這些耗時方法,例如getView(),View#onDraw()等方法,可以很好的幫助我們尋找為什么應用運行緩慢的原因。但有些時候, 還會有一些其他的東西來占用寶貴的CPU資源,而這些資源如果被運用在UI的繪制上,也許我們的應用會更加流暢。Garbage Collector垃圾回收機制會不時的運行,回收那些沒用的對象,通常來講這不會影響我們在前臺運行的程序。但如果GC被運行的過于頻繁,他同樣可以影 響我們應用的執行效率。而我們該如何知道回收的是否過于頻繁了呢…

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!