1. 程式人生 > >就這一次看懂TraceView

就這一次看懂TraceView

一、TraceView的用處

TraceView用於分析計算效能,流入某個方法過於耗時導致UI卡頓,或者某個方法呼叫次數過多,或者某個方法雖然並不佔用太多記憶體但是佔用了大量的CPU資源等等。


二、獲取TraceView檔案的三種方式

1:方式一:通過程式碼獲取

case R.id.bt_trace_view:
    Debug.startMethodTracing("custom");
    startTrace();
    Debug.stopMethodTracing();
    Utils.showToast(this, "成功");
    break;
a:在你想要檢測的某段程式碼的開始位置呼叫
Debug.startMethodTracing("custom");
其中custom可根據需要自定義,其實就是生成的TraceView檔案的檔名,例如custom.trace
b:在你想要檢測的某段程式碼的結束位置呼叫
Debug.stopMethodTracing();
結束檢測沒有引數。
注意:這兩行程式碼要在同一個執行緒成對出現。
c:通過以上設定執行程式碼後就會在手機裡生成/sdcard/custom.trace檔案

d:然後通過adb pull /sdcard/custom.trace D:\folder命令將custom.trace匯出到指定目錄。


後面會著重講開啟.trace檔案且分析.trace檔案的方法。


2:方式二:通過Android Studio的Monitor的CPU模組生成


a:保持app在執行狀態,在你要開始檢測某個功能時點選一下按鈕Start Method Tracing;

b:根據需要操作一下app,再點選該按鈕Stop Method Tracing,則檢測區間結束,Studio會開啟.trace檔案的檢視視窗,
這個.trace檔案在你app專案的captures目錄下。
c:這個視窗介面並不友好,也不好用,我們不直接觀察這個介面,所以我們像方式一一樣將這個.trace檔案儲存到某個目錄。
這裡為了和上面的方式一統一,我也把這個檔案儲存到D:\folder,並且重新命名為custom.trace檔案。
後面會著重講開啟.trace檔案且分析.trace檔案的方法。


3:通過Tools --> Android --> Android Device Monitor獲取

a:通過Tools --> Android --> Android Device Monitor路徑進入,然後參考下圖開始分析;


b:操作一會app後參考下圖結束分析你剛才的操作執行的程式碼;


c:分析完成後會開啟.trace檔案如下圖,這個介面的底部的搜尋功能不可用,而且我們經常需要搜尋功能,所以我的習慣也是
不採用這種方式產生的.trace檔案。

到此,三種獲取.trace檔案的方式講完了,第一種方式和第二種方式我們都獲取到了.trace檔案,第三種方式我一般不採用。


三:分析trace檔案

前面講到的方式一方式二我們獲取到了.trace,下面講解如果開啟並分析該.trace檔案

要開啟.trace檔案就用到了sdk目錄下的D:\sdk\tools\traceview.bat工具,通過如下命令就開啟.trace檔案並進入分析介面


由於通常我們的業務程式碼比較複雜,產生的.trace檔案也很複雜不方便我們學習,所以這裡我自己寫一段非常簡單的程式碼並通過前面方式一講到的通過程式碼的方式獲取.trace檔案來講解。

private void test() {
    Debug.startMethodTracing("custom");
    startTrace();
    Debug.stopMethodTracing();
    Utils.showToast(this, "成功");
}

/**
 * jie1()和jie2()沒有呼叫關係是兄弟關係
 */
private void startTrace() {
    jie1();
    jie2();
}

/**
 * jie2()中兩次呼叫jie3(),其中jie3(0)直接return,不產生遞迴也不會呼叫jie4()
 * jie3(3)會先呼叫一次jie4()再產生3次遞迴呼叫
 */
private void jie2() {
    jie3(0);
    jie3(3);
}

private void jie3(int count) {
    if (count == 3) {
        jie4();
    }
    if (count == 0) {
        return;
    } else {
        jie3(count - 1);
    }
}

/**
 * 故意做比較耗時的操作:用於區分Excl和Incl的關係
 */
private void jie4() {
    for (int i = 0; i < 15; i++) {
        for (int j = 0; j < 15; j++) {
            int k = i + j;
        }
    }
}

private void jie1() {

}

這段程式碼對應的.trace檔案如下圖


其中各個欄位的說明:

Incl Cpu Time%
Incl Cpu Time
某函式佔用的CPU時間,包含內部呼叫其它函式的CPU時間

Excl Cpu Time% 
Excl Cpu Time
某函式佔用的CPU時間,但不含內部呼叫其它函式所佔用的CPU時間
結論:父方法的Incl Cpu Time = 父方法的Excl Cpu Time + 子方法的Incl Cpu Time....  省略號說明一個方法可以呼叫多個方法

Incl Real Time%
Incl Real Time
某函式執行的真實時間(以毫秒為單位),內含呼叫其它函式所佔用的真實時間

Excl Real Time%
Excl Real Time
某函式執行的真實時間(以毫秒為單位),不含呼叫其它函式所佔用的真實時間
結論:父方法的Incl Real Time = 父方法的Excl Real Time + 子方法的Incl Real Time....省略號說明一個方法可以呼叫多個方法

Call+Recur Calls/Total
沒展開某個方法時:
會顯示該方法被程式設計師主動呼叫次數,以及被自身遞迴呼叫次數;例如:2+3,程式設計師呼叫兩次,自己遞迴呼叫3次
展開某個方法時:
父方法會顯示:其呼叫(程式設計師主動呼叫,非遞迴)該方法的次數/該方法的總次數,例如:2/5,說明該方法總共被呼叫5次其中父方法主動調了他2次
子方法會顯示:子方法被其呼叫(程式設計師主動呼叫)的次數/子方法被呼叫的總次數,例如1/5
注意:這裡的程式設計師主動呼叫是指,你看到的程式碼呼叫了幾次就是幾次,並不包括程式碼執行中的遞迴
例如funcA(){
funcD();
funcB();
funcB();
}
這裡程式設計師主動呼叫了兩次funcB()一次funcD(),並沒有管funcB()有沒有遞迴的情況。
再例如funcB(){
funcB();
funcB();
}
這裡funcB()產生了遞迴,但是程式設計師在funcB()中只主動呼叫了2次funcB();不管funcB()遞迴了多少次。

Cpu Time/Call
某函式的Incl Cpu Time與呼叫次數的比。相當於該函式平均執行時間,注意不是Excl Cpu Time;

Real Time/Call
某函式的Incl Real Time與呼叫次數的比。相當於該函式平均執行時間,注意不是Excl Real Time;

注意:這些列可以左右拖動調換位置


通過對TraceView檔案的分析,如果發現某個方法執行時間明顯過長或者呼叫次數異常過多,則就存在優化的可能