<b>本文講的是Pury — 一個新的 Android App 性能分析工具,</b>
<b></b>
手機應用存在的目的,就是在幫助使用者做他們想做的事情的同時,提供最好的使用者體驗 —— 而使用者體驗的重中之重是應用的性能。但有時候開發者們卻以性能為借口,既沒有達到既定目标,又寫着低品質并難以維護的代碼。在這裡我想引用 Michael A. Jackson 的一句話:
“程式優化守則第一條:别去做它。程式優化守則第二條(僅限于專業人員):别去做它,現在還不是時候。”
在開始任何優化之前,我們要先認清問題的症結所在。 第一步,我們先收集和App性能表現的正常資料。比如,從調用startActivity() 到資料顯示在螢幕上的時間。又比如,加載下一頁 RecyclerView 的内容所需要的時間。我們把這個時間和一個可以接受的閥值進行比較就可以發現有沒有什麼問題需要改進了。當應用程式使用的時間比預計的要長的時候,我們就需要深入的檢視并找出是哪些方法(函數)或者API(應用程式接口)出了問題。
幸運的是,我們有一些工具來分析安卓應用程式(的性能):
Android Studio 工具包,比如 System Trace,就是一個非常精确且提供很多資訊的工具。但你需要花很多時間來收集、分析資料。
如果你深入思考一下關于應用程式速度的問題,可以發現其中的一大部分可以被分成兩類:
某個特定方法或者 API 的調用。這類問題可以用 Hugo 一類的軟體來解決。
兩個不同僚件之間的時間。這可能發生在獨立的、但邏輯上關聯的兩段代碼之間。Android Studio 工具包可以解決這個問題,但就像前面說過的,你需要在這上面花很多時間。
當我在搜尋可用的分析工具并思考所有的可能性的時候,我意識到至少一樣工具是沒有的,是以我列出了以下需求:
分析程式的開始和結束應該是兩個獨立觸發的事件,這樣我們就可以按照需求來靈活使用它了。
如果我們想要監視應用的性能表現,僅僅有開始和結束是不夠的。有些時候我們想要知道中間到底發生了什麼。所有關于中間過程的資訊應該被彙總在一個大的報告中。這讓分析和分享資料變得更加簡單。
有時候,有些腳本是經常被重複調用的,比如,為 RecyclerView 加在下一頁的内容。這時候,僅僅對這段腳本進行一次分析是不夠的 —— 我們需要一些統計資料,比如平均、最快和最慢的時間,來進行更深入的研究。
這就是為什麼我開發了 Pury 。
Pury 是一個用來分析多個獨立事件之間的時間的庫。事件可以用注解或者調用方法來觸發。一個腳本的所有事件都被彙總到一個報告中。
用 Pury 打開一個示例應用的輸出:
就像你看到的, Pury 測量應用啟動的時間,包括中間階段,比如在等待螢幕時加載資料和活動生命周期内的方法。每個階段的開始和結束時間以及執行所需的時間。除了正常的分析,它也可以用來監視程式的性能,來確定一些改動不會帶來意外的延遲。
某次運作結果的一個分頁:
在這個例子中,你可以看到, Pury 收集了加載下一頁 5 次的資訊,并輸出了平均值。 Pury 記錄并顯示了每次開始和結束的時間,以及運作的時間。
在深入介紹文檔之前,我想簡單介紹一下 Pury 的内部結構以及它的不足。這會幫助(你們)了解方法的參數以及報錯的資訊。
性能測試都是由 Profiler 來完成的。每個 Profiler 都包含了一個 Runs 清單。多個 Profilers 可以并行運作,但每個 Profiler 隻能同時運作一個 Run 。當一個 Profiler 内所有的 Run 都運作完成時,就會有一個報告自動生成。 Runs 的數量由 runsCounter 參數來決定。
兩個并列運作的 Profilers。第一個隻有一個 Run 并且處于活躍的 stage 中。第二個有一個停止的 Run 和一個活躍的 Run,每個Run 都包含了一個含有兩個 nested stage 的 root stage。活躍的 stage 是綠色的,停止 stage 是紅色的。
Run 内部有一個 root state (根狀态)。每個狀态都有一個名字,一個序列号和一個不限定數量的、嵌套的 nested stage (子狀态)。每個 stage 隻能有一個活躍的 nested stage 。如果你停止了一個 parent stage (父狀态),那麼所有這個狀态的 nested stage 也會停止。
就像之前提到的, Pury 測量多個獨立事件之間的時間。事件可以由注解或調用方法來觸發。以下是三個基本的注解:
1. StartProfiling — 觸發一個事件來啟動 Stage 或者 Run. 分析會在方法運作之前就開始。
StartProfiling 可以接受最多 5 個參數:
profilerName — 分析者的名字将和辨別 Profiler 的 runsCounter 一起顯示在結果中。
runsCounter — Profiler 等待執行的任務的數量。結果隻會在所有任務都完成隻會才會顯示。
stageName — 用來标記一個即将執行的狀态。名字會顯示在結果中。
stageOrder — 顯示狀态順序。新開始的狀态的序号必須大于嵌套最内層活躍狀态的序号。同時,第一個狀态的序号必須是 0。
enabled — 當這個變量的值為“否”時,注解将被略過。
我想強調一點。 Profiler 是由 profilerName 和 runsCounter 組合在一起進行識别的。如果你使用了相同的 profilerName , 但是不同的 runsCounter ,你将會得到兩份獨立的、不同的報告, 而不是一個。
2. StopProfiling — 觸發一個事件來停止 Stage 或 Run. 分析會在方法運作結束後停止。當 Stage 或 Run 停止了,所有 nested stage 都會停止。
它有和 StartProfiling 相同的參數,除了 stageOrder 。
3. MethodProfiling — StartProfiling 和 StopProfiling 的結合。
除了一個小地方需要注意之外,它有和 StartProfiling 相同的參數。 如果 stageName 是空的,那麼它将會有方法的名字和類中産生。這麼做是為了在不輸入參數的情況下使用 MethodProfiling 并得到一個有意義的結果。
因為 Java 7 并不支援可重複的注解,我為以上的注解寫了一個注解集:
就像之前提到的,你可以直接調用一個方法來開始或結束分析:
參數和對應的注解是完全相同的 —— 當然,除了 enabled 。
Pury 使用預設的記錄器,但同時也允許你設定你自己喜歡的記錄器。你要做的就是實作 Logger 端口并在 Pury.setLogger() 中進行設定。
在預設情況下, result 被記錄在 Log.d 中, warning 被記錄在 Log.w 中, error 被記錄在 Log.e 中。
你可以在調試或釋出版本中使用/禁用它。預設設定如下:
第二,包括以下依賴:
如果你想在釋出的時候分析, 在第二個依賴中使用 compile 來代替 compileDebug 。
在沒有設定一些常數的時候,管理多于5個狀态是非常浪費時間的,所有我總是建立一個類,将某個分析情境需要用到的所有東西都集中在這個類裡。就像這樣:
就像你所看到的,每個 ORDER 常數都是基于 parent stage,這樣非常的友善。你還可以給 runsCounter 添加一些常數來保證你每次用的都一樣。你可以添加一個 enabled 标記來輕松的禁用某個特定情境。
<b>原文釋出時間為:2016年10月14日</b>
<b>本文來自雲栖社群合作夥伴掘金,了解相關資訊可以關注掘金網站。</b>