目錄
前言
我們都知道个少,應(yīng)用啟動(dòng)慢锥咸、界面切換慢输硝、動(dòng)畫不流暢卡頓等類似問題基本都是UI刷新不及時(shí)的表現(xiàn)绑谣,UI刷新不及時(shí)就是因?yàn)閁I線程被其他邏輯方法長(zhǎng)時(shí)間占用導(dǎo)致党窜。當(dāng)我們著手解決這些性能問題時(shí),面對(duì)的第一個(gè)問題就是需要找到合適的工具來(lái)檢測(cè)這些問題借宵,通過(guò)各項(xiàng)性能指標(biāo)來(lái)進(jìn)行針對(duì)性的優(yōu)化幌衣,這里我們主要講解Android Studio自帶的性能分析工具CPU Profile
本文基于Android Studio 3.2.1版本
CPU Profile簡(jiǎn)介
CPU Profile是 Android 平臺(tái)配備一個(gè)很好的性能分析的工具。它可以通過(guò)圖形化的方式讓我們了解我們要跟蹤的程序的性能暇务,并且能具體到 method泼掠,它可以加載 trace 文件,用圖形的形式展示代碼的執(zhí)行時(shí)間垦细、次數(shù)及調(diào)用棧择镇,便于我們分析,幫助我們最大限度減少應(yīng)用的 CPU 使用率
trace 文件是 log 信息文件的一種括改,可以通過(guò)Debug類生成
生成Trace文件
1.通過(guò)代碼生成
public void fun() {
//開始trace腻豌,保存文件到 /sdcard/funTrace.trace
Debug.startMethodTracing("funTrace");
//想要監(jiān)測(cè)的代碼
....
//結(jié)束trace
Debug.stopMethodTracing();
}
第一步:通過(guò)Debug.startMethodTracing("funTrace");
開始產(chǎn)生trace數(shù)據(jù),通過(guò) Debug.stopMethodTracing();
結(jié)束監(jiān)測(cè),生成trace文件(如果系統(tǒng)達(dá)到最大緩存大小時(shí)吝梅,還沒有調(diào)用stopMethodTracing()虱疏,系統(tǒng)會(huì)停止跟蹤并發(fā)送一個(gè)通知)
第二步:通過(guò)adb命令 adb pull /sdcard/funTrace.trace /Users/geekholt/Downloads
將trace文件導(dǎo)出到電腦
第三步:將trace文件直接拖到Android Studio中進(jìn)行分析
通過(guò)代碼生成trace文件更容易控制開始點(diǎn)和結(jié)束點(diǎn),且可以導(dǎo)出trace文件苏携。但是這種方式運(yùn)行時(shí)開銷嚴(yán)重做瞪,程序方法執(zhí)行整體都會(huì)變慢,可能會(huì)帶偏優(yōu)化方向
- 使用CPU Profile
第一步:點(diǎn)擊Android studio工具欄中的Profile右冻,運(yùn)行項(xiàng)目
第二步:選擇CPU Profile装蓬,點(diǎn)擊Record開始記錄,相當(dāng)于執(zhí)行代碼Debug.startMethodTracing();
,再點(diǎn)一次停止記錄,相當(dāng)于執(zhí)行代碼 Debug.stopMethodTracing();
伍伤,這樣就生成了trace文件
第三步:生成trace文件后,Android Studio會(huì)自動(dòng)加載trace文件暗赶,并展示圖形化界面
Trace文件分析
默認(rèn)視圖分析
當(dāng)您打開 CPU Profiler 時(shí),它將立即開始顯示應(yīng)用的 CPU 使用率和線程 Activity肃叶。 您應(yīng)該會(huì)看到類似圖 1 的一些內(nèi)容:
如圖 1 所示蹂随,CPU Profiler 的默認(rèn)視圖包括以下內(nèi)容:
Event 時(shí)間線: 顯示應(yīng)用中在其生命周期不同狀態(tài)間轉(zhuǎn)換的 Activity,并表明用戶與設(shè)備的交互被环,包括屏幕旋轉(zhuǎn) Event
CPU 時(shí)間線: 顯示應(yīng)用的實(shí)時(shí) CPU 使用率(以占總可用 CPU 時(shí)間的百分比表示)以及應(yīng)用使用的總線程數(shù)糙及。 此時(shí)間線還顯示其他進(jìn)程的 CPU 使用率(如系統(tǒng)進(jìn)程或其他應(yīng)用),以便您可以將其與您的應(yīng)用使用率進(jìn)行對(duì)比筛欢。 通過(guò)沿時(shí)間線的水平軸移動(dòng)鼠標(biāo)浸锨,您還可以檢查歷史 CPU 使用率數(shù)據(jù)
-
線程 Activity 時(shí)間線:
列出屬于應(yīng)用進(jìn)程的每個(gè)線程并使用下面列出的顏色沿時(shí)間線標(biāo)示它們的 Activity。 在您記錄一個(gè)函數(shù)跟蹤后版姑,您可以從此時(shí)間線中選擇一個(gè)線程以在跟蹤窗格中檢查其數(shù)據(jù)
綠色: 表示線程處于活動(dòng)狀態(tài)或準(zhǔn)備使用 CPU柱搜。 即,它正在“運(yùn)行中”或處于“可運(yùn)行”狀態(tài)
黃色: 表示線程處于活動(dòng)狀態(tài)剥险,但它正在等待一個(gè) I/O 操作(如磁盤或網(wǎng)絡(luò) I/O)聪蘸,然后才能完成它的工作
灰色: 表示線程正在休眠且沒有消耗任何 CPU 時(shí)間。 當(dāng)線程需要訪問尚不可用的資源時(shí)偶爾會(huì)發(fā)生這種情況表制。 線程進(jìn)入自主休眠或內(nèi)核將此線程置于休眠狀態(tài)健爬,直到所需的資源可用
trace文件視圖分析
- 選擇時(shí)間范圍: 用于確定您要在跟蹤窗格中檢查所記錄時(shí)間范圍的哪一部分。 當(dāng)您首次記錄函數(shù)跟蹤時(shí)么介,CPU Profiler 將在 CPU 時(shí)間線中自動(dòng)選擇您的記錄的完整長(zhǎng)度娜遵。 如果您想僅檢查所記錄時(shí)間范圍一小部分的函數(shù)跟蹤數(shù)據(jù),您可以點(diǎn)擊并拖動(dòng)突出顯示的區(qū)域邊緣以修改其長(zhǎng)度
- 時(shí)間戳: 用于表示所記錄函數(shù)跟蹤的開始和結(jié)束時(shí)間(相對(duì)于分析器從設(shè)備開始收集 CPU 使用率信息的時(shí)間)壤短。 在選擇時(shí)間范圍時(shí)慨仿,您可以點(diǎn)擊時(shí)間戳以自動(dòng)選擇完整記錄,如果您有多個(gè)要進(jìn)行切換的記錄镰吆,則此做法尤其有用
- 跟蹤窗格: 用于顯示您所選的時(shí)間范圍和線程的函數(shù)跟蹤數(shù)據(jù)跑慕。 僅在您至少記錄一個(gè)函數(shù)跟蹤后此窗格才會(huì)顯示。 在此窗格中核行,您可以選擇想如何查看每個(gè)堆疊追蹤(使用跟蹤標(biāo)簽),以及如何測(cè)量執(zhí)行時(shí)間(使用時(shí)間引用下拉菜單)
- 選擇后,可通過(guò) Top Down 樹婆赠、Bottom Up 樹、調(diào)用圖表或火焰圖的形式顯示您的函數(shù)跟蹤休里。 您可以在下文中了解每個(gè)跟蹤窗格標(biāo)簽的更多信息
- 從下拉菜單中選擇以下選項(xiàng)之一,以確定如何測(cè)量每個(gè)函數(shù)調(diào)用的時(shí)間信息:
- Wall clock time:壁鐘時(shí)間信息表示實(shí)際經(jīng)過(guò)的時(shí)間
- Thread time:線程時(shí)間信息表示實(shí)際經(jīng)過(guò)的時(shí)間減去線程沒有消耗 CPU 資源的任意時(shí)間部分悴侵。 對(duì)于任何給定函數(shù)拭嫁,其線程時(shí)間始終少于或等于其壁鐘時(shí)間。 使用線程時(shí)間可以讓您更好地了解線程的實(shí)際 CPU 使用率中有多少是給定函數(shù)消耗的
使用 Call Chart 標(biāo)簽檢查跟蹤
Call Chart 標(biāo)簽提供函數(shù)跟蹤的圖形表示形式做粤,其中,水平軸表示函數(shù)調(diào)用(或調(diào)用方)的時(shí)間段和時(shí)間妇垢,并沿垂直軸顯示其被調(diào)用者。 對(duì)系統(tǒng) API 的函數(shù)調(diào)用顯示為橙色闯估,對(duì)應(yīng)用自有函數(shù)的調(diào)用顯示為綠色吼和,對(duì)第三方 API(包括 Java 語(yǔ)言 API)的函數(shù)調(diào)用顯示為藍(lán)色。 下面的圖 3 展示了一個(gè)調(diào)用圖表示例尤辱,并描繪了給定函數(shù)的 self time砂豌、children time 以及總時(shí)間的概念
提示: 若要跳轉(zhuǎn)到某個(gè)函數(shù)的源代碼,請(qǐng)右鍵點(diǎn)擊該函數(shù)并選擇 Jump to Source
使用 Flame Chart 標(biāo)簽檢查跟蹤
Flame Chart 標(biāo)簽提供一個(gè)倒置的調(diào)用圖表咖熟,其匯總相同的調(diào)用堆棧。 即馍管,收集共享相同調(diào)用方順序的完全相同的函數(shù)薪韩,并在火焰圖中用一個(gè)較長(zhǎng)的橫條表示它們(而不是將它們顯示為多個(gè)較短的橫條,如調(diào)用圖表中所示)俘陷。 這樣更方便您查看哪些函數(shù)消耗最多時(shí)間。 不過(guò)桨菜,這也意味著水平軸不再代表時(shí)間線捉偏,相反,它表示每個(gè)函數(shù)相對(duì)的執(zhí)行時(shí)間
為幫助說(shuō)明此概念告私,請(qǐng)考慮以下圖 4 中的調(diào)用圖表。 請(qǐng)注意驻粟,函數(shù) D 多次調(diào)用 B(B1根悼、B2 和 B3)挤巡,其中一些對(duì) B 的調(diào)用也調(diào)用了 C(C1 和 C3)
由于 B1酷麦、B2 和 B3 共享相同的調(diào)用方順序 (A → D → B),因此沃饶,可將它們匯總在一起轻黑,如下所示氓鄙。 同樣,將 C1 和 C3 匯總在一起抖拦,因?yàn)樗鼈円补蚕硐嗤恼{(diào)用方順序 (A → D → B → C)—請(qǐng)注意舷暮,未包含 C2,因?yàn)樗哂胁煌恼{(diào)用方順序 (A → D → C)
匯總的函數(shù)調(diào)用用于創(chuàng)建火焰圖下面,如圖 6 所示。請(qǐng)注意沥割,對(duì)于火焰圖中任何給定的函數(shù)調(diào)用,消耗最多 CPU 時(shí)間的被調(diào)用方首先顯示
使用 Top Down 和 Bottom Up 檢查跟蹤
Top Down 標(biāo)簽顯示一個(gè)函數(shù)調(diào)用列表,在該列表中展開函數(shù)節(jié)點(diǎn)會(huì)顯示函數(shù)的被調(diào)用方蓄髓。 圖 7 顯示圖 3 中調(diào)用圖表的“Top Down”圖表。圖表中的每個(gè)箭頭都從調(diào)用方指向被調(diào)用方
如圖 7 所示会喝,在“Top Down”標(biāo)簽中展開函數(shù) A 的節(jié)點(diǎn)可顯示它的被調(diào)用方,即函數(shù) B 和 D肢执。 然后,展開函數(shù) D 的節(jié)點(diǎn)可顯示它的被調(diào)用方预茄,即函數(shù) B 和 C 等等。 與 Flame chart 標(biāo)簽相似耻陕,“Top Down”樹匯總共享相同調(diào)用堆棧的相同函數(shù)的跟蹤信息。 也就是說(shuō)膘怕,Flame chart 標(biāo)簽可提供Top down 標(biāo)簽的圖形化表示形式
Top Down 標(biāo)簽提供以下信息以幫助說(shuō)明在每個(gè)函數(shù)調(diào)用上所花費(fèi)的 CPU 時(shí)間(時(shí)間也可以用線程總時(shí)間占所選時(shí)間范圍的持續(xù)時(shí)間的百分比表示):
- Self: 表示函數(shù)調(diào)用在執(zhí)行自己的代碼(而非被調(diào)用方的代碼)上所花的時(shí)間召庞,如圖 3 中的函數(shù) D 所示
- Children: 表示函數(shù)調(diào)用在執(zhí)行自己的被調(diào)用方(而非自己的代碼)上所花的時(shí)間来破,如圖 3 中的函數(shù) D 所示
- 總和: 函數(shù)的 Self 和 Children 時(shí)間的總和。 這表示應(yīng)用在執(zhí)行函數(shù)調(diào)用上所花的總時(shí)間徘禁,如圖 3 中函數(shù) D 所示
Bottom Up 標(biāo)簽顯示一個(gè)函數(shù)調(diào)用列表晌坤,在該列表中展開函數(shù)節(jié)點(diǎn)將顯示函數(shù)的調(diào)用方。 通過(guò)使用圖 7 中展示的跟蹤示例骤菠,圖 7 為函數(shù) C 的Bottom Up”樹疤孕。 在“Bottom Up”樹中打開函數(shù) C 的節(jié)點(diǎn)可顯示它獨(dú)有的調(diào)用方,即函數(shù) B 和 D祭阀。 請(qǐng)注意鹉戚,盡管 B 調(diào)用 C 兩次专控,但在“Bottom Up”樹中展開函數(shù) C 的節(jié)點(diǎn)時(shí),B 僅顯示一次伦腐。 然后,展開 B 的節(jié)點(diǎn)顯示其調(diào)用方幸冻,即函數(shù) A 和 D
Bottom Up 標(biāo)簽用于按照消耗最多(最少)CPU 時(shí)間排序函數(shù)咳焚。 您可以檢查每個(gè)節(jié)點(diǎn)以確定在調(diào)用函數(shù)上哪些調(diào)用方花了最多 CPU 時(shí)間洽损。 與“Top Down”樹相比碑定,“Bottom Up”樹中的每個(gè)函數(shù)的時(shí)間信息引用每個(gè)樹頂部的函數(shù)(頂部模式)又官。 CPU 時(shí)間也可表示為在該記錄期間占線程總時(shí)間的百分比。 下表有助于闡述如何解釋頂部節(jié)點(diǎn)的時(shí)間信息及其調(diào)用方函數(shù)(子節(jié)點(diǎn))
Self | Children | 合計(jì) | |
---|---|---|---|
“Bottom Up”樹頂部的函數(shù)(頂部模式) | 表示函數(shù)在執(zhí)行自己的代碼(而非其被調(diào)用方的代碼)上所花的時(shí)間赏胚。 與“Top Down”樹相比,此時(shí)間信息表示在記錄的持續(xù)時(shí)間內(nèi)對(duì)此函數(shù)所有調(diào)用的總和崖疤。 | 表示函數(shù)執(zhí)行它的被調(diào)用方(而非它自己的代碼)上所花的總時(shí)間。 與“Top Down”樹相比劫哼,此時(shí)間信息表示在記錄的持續(xù)期間內(nèi)所有對(duì)此函數(shù)被調(diào)用方的調(diào)用總和。 | self time 和 children time 的總和权烧。 |
調(diào)用方函數(shù)(子節(jié)點(diǎn)) | 表示在由調(diào)用方調(diào)用時(shí)被調(diào)用方的總 self time。 以圖 7 中的“Bottom Up”樹為例妻率,被 B 調(diào)用時(shí)板祝,函數(shù) B 的 self time 將等于函數(shù) C 每個(gè)執(zhí)行的 self time 的總和。 | 表示在由調(diào)用方調(diào)用時(shí)被調(diào)用方的總 children time券时。 以圖 7 中的“Bottom Up”樹為例,被 B 調(diào)用時(shí)橘洞,函數(shù) B 的 children time 將等于函數(shù) C 每個(gè)執(zhí)行的 children time 的總和。 | self time 和 children time 的總和炸枣。 |