前言android
性能優化的過程分兩部分:性能優化
發現性能瓶頸併發
制定方案,解決性能問題app
解決性能問題的方案須要具體狀況具體分析,並無徹底固定的路子,更多的是靠經驗的積累,本文不作涉及。可是發現性能瓶頸確實有着固定的方法。本文主要介紹 如何找到性能瓶頸 。ide
如何找到性能瓶頸函數
經常使用的性能檢測工具是traceview,集成於 Android Device Monitor 中。從Android Studio3.0開始, Android Device Monitor 被廢棄,取而代之的是 Android Profiler ,其中提供了 Memory Prodiler 、CPU Profiler、Network Prodiler三大功能。工具
內存優化(包括內存泄漏)經常使用的是 MAT 或者 LeakCanary ,而 Memory Profiler 至關於將 MAT 的簡化版功能集成到 AS 中。相對的在性能優化方面,CPU Profiler 至關於將 traceview 的功能集成到了 AS 中。性能
因此,使用AS3.0以前版本的,可使用traceview,而使用AS3.0之後版本的,除了traceview,還能夠選擇CPU Profiler。測試
若是想追蹤系統進程的詳細數據,以解決幀引發的界面卡頓等問題,可使用 systrace ,本文不作涉及。優化
traceview 使用方法
使用 traceview 須要首先使用 Debug 類進行 插樁 ,當應用執行到被插樁的代碼時就會在手機sdcard中自動生成 .trace 文件,以後使用 traceview 或者 AS(3.0以上版本)打開文件便可。
1、插樁
插樁須要使用到 Debug 類,而且會在 sdcard 中生成 .trace 文件,因此你必須首先保證你的應用具備寫外部存儲( WRITE_EXTERNAL_STORAGE )的權限。
在想要跟蹤的代碼邏輯開頭和結尾處分別插樁:
// Starts recording a trace log with the name you provide. For example, the
// following code tells the system to start recording a .trace file to the
// device with the name "sample.trace".
Debug.startMethodTracing("sample");
...
// The system begins buffering the generated trace data, until your
// application calls stopMethodTracing(), at which time it writes
// the buffered data to the output file.
Debug.stopMethodTracing();
生成的 .trace
文件會被保存在固定目錄下,與 getExternalFilesDir()
返回的目錄相同,即 /sdcard/Android/data/[YOUR_PACKAGE_NAME]/files
下。
請注意,若是您的應用在未更改跟蹤日誌名稱的狀況下再次調用 startMethodTracing(),則會覆蓋已保存至設備的現有日誌。若是但願每次運行都保存至不一樣的日誌文件,可使用以下代碼:
// Uses the SimpleDateFormat class to create a String with
// the current date and time.
SimpleDateFormat date =
new SimpleDateFormat("dd_MM_yyyy_hh_mm_ss");
String logDate = date.format(new Date());
// Applies the date and time to the name of the trace log.
Debug.startMethodTracing(
"sample-" + logDate);
若是系統在您調用 stopMethodTracing() 以前達到最大緩衝值,則會中止跟蹤並向管理中心發送通知。 開始和中止跟蹤的函數在您的整個應用流程內均有效。 也就是說,您能夠在 Activity 的 onCreate(Bundle)
函數中調用 startMethodTracing(),在 Activity 的 onDestroy() 函數中調用 stopMethodTracing()。
2、查看 .trace 文件
插好樁後,安裝應用並運行被檢測部分的功能,而後就能夠經過 AS 或者 traceview 查看文件了。
使用 AS 查看
在AS中點擊 View - Tool Windows - Android File Explorer 打開 Android File Explorer :
在 /sdcard/Android/data/[YOUR_PACKAGE_NAME]/files 下便可找到生成的 .trace 文件,雙擊文件便可打開。
將 .trace 文件保存至電腦,直接拖入AS窗口,也可直接打開該視圖。
在打開的視圖中,左上方能夠選擇想要查看的線程。能夠查看監控期間指定線程運行了多久、執行了哪些方法、每一個方法執行了多久等等。
其中有4個名詞須要解釋一下:
使用 traceview 查看
要使用 traceview 查看,須要首先將 .trace 文件保存到電腦:
adb pull /sdcard/Android/data/[YOUR_PACKAGE_NAME]/files/sample.trace D:Documentssample.trace
打開 Android Device Monitor
。AS3.0之前的版本,就是LogCat所在的窗口,再切換一下tab頁便可。AS3.0之後,進入 android-sdk/tools/
路徑,運行如下命令:
monitor
雖然 Android Device Monitor 的 DDMS 也有 File Explorer ,可是未 root 的手機,查看不到上述路徑,所以只能將 .trace 文件保存到電腦查看。
在 Android Device Monitor 中,依次點擊 File - Open File
,選擇 .trace 文件路徑便可打開:
內容與AS打開時相似,相差較大的主要是圖標部分,沒有AS的 Call Chart 直觀形象。
其中也有4個概念:
Cpu Time:至關於AS中的 Thread time。
Real Time:至關於AS中的Wall Clock Time。
Inclusive Time:同AS同樣。
Exclusive Time:同AS同樣。
使用 AS 查看仍是使用 traceview 查看
這個就見仁見智了,根據我我的使用的感受來看,建議使用AS查看。緣由有二:
AS更簡單。不須要單獨打開ADM,更不須要將 .trace 文件保存到電腦。
AS的調用圖( Call Chart )更加直觀,cpu時間的消耗一目瞭然。
Call Chart 的水平軸表示函數調用(或調用方)的時間段和時間,並沿垂直軸顯示其被調用者。 下圖展現了一個調用圖表示例,並描繪了給定函數的 self time、children time 以及總時間的概念。
最後須要注意一點,跟蹤分析過程當中,應用的運行速度會減慢。因此,經過 traceview 獲得的分析數據並不能精確反應某個方法在實際執行時的絕對時間。關於這一點,在最後的注意事項中再作詳細分析。
Google還提供了基於樣本的分析方式,以減小分析對運行時性能的影響。要啓用樣本分析,需調用 Debug.startMethodTracingSampling() 方法(而非 Debug.startMethodTracing() 方法)。系統會按期收集樣本,直至調用 stopMethodTracing() 。
CPU Profiler 使用方法
使用 CPU Profiler 進行函數跟蹤比 traceview 更簡單。不須要作任何代碼上的植入,下面作一個簡單的介紹:
首先,經過
View - Tool Windows - Android Profiler 打開 Android Profiler 。手機鏈接電腦後運行應用,在 Android Profiler 中會看到如下視圖:
左上角能夠選擇設備和進程,點擊 CPU 區域,便可進入CPU Profiler視圖:
左上角能夠選擇跟蹤模式:
Sampled:按默認採樣率捕獲應用的調用堆棧。該模式的固有問題是,若是應用在一次捕獲後進入一個函數並在下一次捕獲前退出該函數,則分析器不會記錄該函數調用。若是對此類生命週期很短的跟蹤函數感興趣,可使用「Instrumented」跟蹤。
Instrumented:以在每一個函數調用的開始和結束時記錄時間戳。 分析比較時間戳,以生成函數跟蹤數據。 須要注意的是,設置與函數關聯的開銷會影響運行時性能,甚至分析數據,對於生命週期相對較短的函數,這一點更爲明顯。 此外,若是應用短期內執行大量函數,則分析器可能會迅速超出它的文件大小限制,且不能再記錄更多跟蹤數據。
Edit configurations:自定義採樣率。與 traceview 中的 Debug.startMethodTracingSampling() 相似。
.trace 文件的大小是有限制的。對於給定錄製,當分析器到達該限制時,AS 將中止收集新數據(不過,這不會中止記錄)。 在執行「Instrumented」跟蹤時,這種狀況一般會更快發生,由於與「Sampled」跟蹤相比,此類跟蹤在較短期裏會收集更多數據。
若是你使用的是Android 8.0(API 26)或更高版本的設備,則對於跟蹤數據的文件大小沒有限制,此值可忽略。不過,你仍需留意每次記錄後設備收集了多少數據,由於 AS 可能難以解析大型跟蹤文件。
點擊上方的「開始錄製」按鈕,而後在應用中操做執行被追蹤的功能,結束後再點擊「中止錄製」按鈕。CPU Profiler 會自動開始分析並生成數據。
以上就是 CPU Profiler 和 traceview 的使用方法。至於如何制定優化方案,就不展開了,並無徹底固定的路子。就我本例的 onRebuild() 方法而言,是針對耗時的Contact構造過程作了並行處理,將上百個有序的構造過程平分到5個線程中併發執行,而後再按順序合併數據到一個線程中。最終 onRebuild() 執行速度從15秒提高到了2.5秒,對我來講已經夠用了。
重要注意事項
不管是使用 traceview 仍是 CPU Profiler 進行函數跟蹤,有一點須要注意:跟蹤分析過程當中,應用的運行速度會減慢。因此,分析數據並不能精確反應某個方法在實際執行時的絕對時間。下面是我在優化項目中的 onRebuild(boolean) 方法時,記錄的4組數據,讓咱們來對比一下:
實際執行時間:不啓用分析模式,正常運行狀態下經過打印日誌獲得的實際執行時間。
Profiler統計時間:使用 CPU Profiler 分析得到的執行時間。
traceview統計時間:經過分析 traceview 產生的 .trace 文件,從中得到的執行時間。
traceview實際時間:使用 traceview 的狀況下,經過打印日誌獲得的實際執行時間。
爲何針對 traceview 會例舉兩個時間呢?這是由於測試過程當中發現 traceview 自動分析出來的時間比 實際執行時間 不只沒有慢,反而快了不少,疑惑下又在啓用 traceview 的狀況下經過如下代碼測算了一下實際的時間,這個卻是真的比 實際執行時間 慢了。
Debug.startMethodTracing("smssdk_onrebuild");
long curr = System.currentTimeMillis();
onRebuild(true);
Log.d(TAG, "onRebuild lasts: " + (System.currentTimeMillis() - curr));
Debug.stopMethodTracing();
從上表數據可見,不管是 CPU Profiler 仍是 traceview ,
統計出來的時間都不能準確表明實際執行時間。更甚者,traceview自動分析出來的數據也與traceview跟蹤模式下實際的時間有巨大差異,關於這一點,我沒找到詳細的解釋,若是有人知道,還望不吝賜教。
既然跟蹤分析獲得的時間都不能表示實際的時間,那麼這些數據是否是沒用呢?固然不是!它們至少在如下兩個方面具備價值:
經過這些工具跟蹤函數,也只能作一個相對的參考,並不能徹底正確的反應函數的執行性能。好比我經過 CPU Profiler 得到的 onRebuild() 方法的分析數據顯示,整個執行過程當中 Contact 的構造方法佔了60%左右,Contact.toString() 方法佔了40%左右,但實際上在 onRebuild() 方法消耗的15秒中,Contact.toString() 只消耗了百毫秒級,而九成以上時間都被其構造方法消耗了,說明 CPU Profiler 的監控過程對 Contact.toString() 的性能產生了更大的影響。
而一樣的問題卻並無出如今 traceview 的分析結果中。
請注意,CPU Profiler 和 traceview 不能同時使用,若是代碼中植入了插樁的代碼,則有可能致使 CPU Profiler 沒法正常開始或中止錄製。
traceview 和 CPU Profiler 的對比
從用法上來看,traceview 比 CPU Profiler 稍微複雜一點。相似於MAT須要首先獲取 .hprof 堆轉儲文件,traceview 也要首先獲取 .trace 文件,而後使用traceview分析該文件。而 CPU Profiler 則能夠直接對應用進行分析。
從最終生成的圖表上來看,CPU Profiler 生成的圖表有 Call Chart、Flame Chart ,它們能夠很是形象的表示出線程內執行了哪些函數,函數的執行時間,調用棧等等,一目瞭然,並且在任意函數上點擊右鍵,能夠直接跳轉至對應的代碼,很是方便,在這一點上,相對於 traceview 要優秀。