Systrace?響應(yīng)速度實(shí)戰(zhàn)?2?:響應(yīng)速度實(shí)戰(zhàn)分析?-?以啟動(dòng)速度為例
1. 準(zhǔn)備工作
這個(gè)案例和對(duì)應(yīng)的 Systrace 偏工程化一些,省略了很多細(xì)節(jié),因?yàn)閼?yīng)用的啟動(dòng)流程涉及的知識(shí)非常廣,如果每個(gè)都細(xì)化的話,會(huì)有很大的篇幅。推薦大家看這篇文章,非常詳細(xì):Android 應(yīng)用啟動(dòng)全流程分析[2]
- 打開 Binder 調(diào)試,方便在 Trace 中顯示 Binder 信息?(即可以在 Systrace 中看到 Binder 調(diào)用的函數(shù))- 需要 Root
-
開啟 ipc debug:
adb shell am trace-ipc start
-
抓取結(jié)束后,可以執(zhí)行下面的命令關(guān)閉?
adb shell am trace-ipc stop --dump-file /data/local/tmp/ipc-trace.txt
-
Trace 命令加入 irq tag,默認(rèn)的命令不包含 irq,需要自己加 irq 的 TAG,這樣打開 Trace 之后,就可以看到 irq 相關(guān)的內(nèi)容,最后的抓 trace 命令如下:? ?
python /mnt/d/Android/platform-tools/systrace/systrace.py gfx input view webview wm am sm rs bionic power pm ss database network adb idle pdx sched irq freq idle disk workq binder_driver binder_lock -a com.xxx.xxx,注意這里的 com.xxx.xxx 換成自己的包名,如果不是調(diào)試特定的包名,可以去掉 -a com.xxx.xxx
-
推薦 :如果要 Debug 的 App 可以進(jìn)行編譯(即可以使用 Gradle 編譯,一般自己開發(fā)的項(xiàng)目都可以),可以在分析響應(yīng)速度問(wèn)題的時(shí)候,引入 TraceFix 庫(kù)(接入方法參考 https://github.com/Gracker/TraceFix)。接入之后,編譯的時(shí)候就會(huì)進(jìn)行代碼插樁,在 App 代碼的每一個(gè)函數(shù)中都插入 Trace 點(diǎn),這樣在分析的時(shí)候可以看到更詳細(xì)的 App 的信息
-
使用插件前,只能看到 Framework 里面的 Trace 點(diǎn)
-
使用插件后?,可以看到 Trace 中顯示的信息多了很多(App 自身的代碼邏輯,F(xiàn)ramework 的代碼沒(méi)法插樁)
2. Android App 冷啟動(dòng)流程分析
2. Android App 冷啟動(dòng)流程分析
本文以 在桌面上冷啟動(dòng)一個(gè) Android App 為例,應(yīng)用冷啟動(dòng)的整個(gè)流程包含了從用戶觸摸屏幕到應(yīng)用完全顯示的整個(gè)流程,其中涉及到
-
觸摸屏中斷處理階段
-
InputReader 和 InputDispatcher 處理 input 事件階段
-
Launcher 處理 input 事件階段
-
SystemServer 處理啟動(dòng)事件
-
啟動(dòng)動(dòng)畫
-
應(yīng)用啟動(dòng)和自身邏輯階段
上一篇文章有講到響應(yīng)速度問(wèn)題,需要搞清楚 起點(diǎn) 和 終點(diǎn),對(duì)于應(yīng)用冷啟動(dòng)來(lái)說(shuō),起點(diǎn)就是 input 事件,終點(diǎn)就是應(yīng)用完全展示給用戶(用戶可操作)
下面將從上面幾個(gè)關(guān)鍵流程,通過(guò) Systrace 的來(lái)介紹整個(gè)流程
2.1 觸摸屏中斷處理階段
由于我們的案例是在桌面冷啟動(dòng)一個(gè) App,那么在手指觸摸手機(jī)屏幕的時(shí)候,觸摸屏?xí)|發(fā)中斷,這個(gè)中斷我們最早能在 Systrace 中看到的地方如下:
對(duì)應(yīng)的 cpu ss 區(qū)域和 中斷區(qū)域(加了 irq 的 tag 才可以看到)
一般來(lái)說(shuō),點(diǎn)擊屏幕會(huì)觸發(fā)若干個(gè)中斷,這些信號(hào)經(jīng)過(guò)處理之后,觸摸屏驅(qū)動(dòng)會(huì)把這些點(diǎn)更新到 EventHub 中,讓 InputReader 和 InputDIspatcher 進(jìn)行進(jìn)一步的處理。這一步一般不會(huì)出現(xiàn)什么問(wèn)題,廠商這邊對(duì)觸摸屏的調(diào)教可能會(huì)關(guān)注這里
2.2 InputReader 和 InputDispatcher 處理 Input 事件階段
InputReader 和 InputDispatcher 這兩個(gè)線程跑在 SystemServer 里面,專門負(fù)責(zé)處理 Input 事件,具體的流程可以參考Android Systrace 基礎(chǔ)知識(shí) - Input 解讀[3] 這篇文章
InputReader 和 InputDispatcher這里由于我們是點(diǎn)擊桌面上的一個(gè) App 的圖標(biāo),可以看到底層上報(bào)上來(lái)的事件包括一個(gè) Input_Down 事件 若干個(gè) Input Move 事件 一個(gè) Input Up 事件,組成了一個(gè)完整的點(diǎn)擊事件
由于 Launcher 在進(jìn)程創(chuàng)建的時(shí)候就注冊(cè)了 Input 監(jiān)聽(tīng),且此時(shí) Launcher 在前臺(tái)且可見(jiàn),所以 Launcher 進(jìn)程可以收到這些 Input 事件,并根據(jù) Input 事件的類型進(jìn)行處理,input 事件在 SystemServer 和 App 的流轉(zhuǎn)在 Systrace 中的具體表現(xiàn)可以參考 Android Systrace 基礎(chǔ)知識(shí) - Input 解讀[4] ,這里把核心的兩張圖放上來(lái)
2.2.1 Input 事件在 SystemServer 中流轉(zhuǎn)
看下圖即可,如果要看更詳細(xì)的,可以查看 Android Systrace 基礎(chǔ)知識(shí) - Input 解讀[5]
Input 事件在 SystemServer 中流轉(zhuǎn)
2.2.2 Input 事件在 Launcher 進(jìn)程流轉(zhuǎn)
看下圖即可,如果要看更詳細(xì)的,可以查看 Android Systrace 基礎(chǔ)知識(shí) - Input 解讀[6]
2.3 Launcher 進(jìn)程處理 Input 事件階段
Launcher 處理 Input 事件也是響應(yīng)時(shí)間的一個(gè)重要階段,主要包括兩個(gè)響應(yīng)速度指標(biāo)
-
點(diǎn)擊桌面到桌面第一幀響應(yīng)(一般 Launcher 會(huì)在接收到 Down 事件的時(shí)候,將 App 圖標(biāo)置灰,以表示接收到了事件;有的定制桌面 App 圖標(biāo)會(huì)有一個(gè)縮小的動(dòng)畫,表示被按壓)
-
桌面第一幀響應(yīng)到啟動(dòng) App(這段時(shí)間指的是桌面在收到 Down 對(duì) App 圖標(biāo)做處理后,到收到 Up 事件判斷需要啟動(dòng) App 的時(shí)間)
另外提一下,滑動(dòng)桌面到桌面第一幀響應(yīng)時(shí)間(這個(gè)指的是滑動(dòng)桌面的場(chǎng)景,左右滑動(dòng)桌面的時(shí)候,用高速相機(jī)拍攝,從手指動(dòng)開始,到桌面動(dòng)的第一幀的時(shí)間)也是一個(gè)很重要的響應(yīng)速度指標(biāo),部分廠商也會(huì)在這方面做優(yōu)化,感興趣的可以自己試試主流廠商的桌面滑動(dòng)場(chǎng)景(跟原生的機(jī)器對(duì)比 Systrace 即可)
在冷啟動(dòng)的場(chǎng)景里面,Launcher 在收到 up 事件后,會(huì)進(jìn)行邏輯判斷,然后啟動(dòng)對(duì)應(yīng)的 App(這里主要是交給 AMS 來(lái)處理,又回到了 SystemServer 進(jìn)程)
Launcher 處理 input 事件這個(gè)階段通常也是做系統(tǒng)優(yōu)化的會(huì)比較關(guān)注,做 App 的同學(xué)還不需要關(guān)注到這里(Launcher App 的除外);另外在最新的版本,應(yīng)用啟動(dòng)的動(dòng)畫是由 Launcher 和 SystemServer 共同完成的,目的就是可以做一些復(fù)雜的動(dòng)畫而沒(méi)有割裂感,大家可以用慢鏡頭拍一下啟動(dòng)時(shí)候和退出應(yīng)用的動(dòng)畫,可以看到有的應(yīng)用圖標(biāo)是分層的,甚至?xí)?dòng),這是之前純粹由 SystemServer 這邊來(lái)做動(dòng)畫所辦不到的
2.4 SystemServer 處理 StartActivity 階段
SystemServer 處理主要是有2部分
-
處理啟動(dòng)命令
-
通知 Launcher 進(jìn)入 Pause 狀態(tài)
-
fork 新的進(jìn)程
處理啟動(dòng)命令
這個(gè) SystemServer 進(jìn)程中的 Binder 調(diào)用就是 Launcher 通過(guò) ActivityTaskManager.getService().startActivity 調(diào)用過(guò)來(lái)的
fork 新的進(jìn)程,則是在判斷啟動(dòng)的 Activity 的 App 進(jìn)程沒(méi)有啟動(dòng)后,需要首先啟動(dòng)進(jìn)程,然后再啟動(dòng) Activity,這里是冷啟動(dòng)和其他啟動(dòng)不一樣的地方。fork 主要是 fork Zygote64 這個(gè)進(jìn)程(部分 App 是 fork 的 Zygote32 )
fork 新進(jìn)程fork 新進(jìn)程對(duì)應(yīng)的代碼
Zygote 64 位進(jìn)程執(zhí)行 Fork 操作
對(duì)應(yīng)的 App 進(jìn)程出現(xiàn)
對(duì)應(yīng)的代碼如下,這里就正式進(jìn)入了 App 自己的進(jìn)程邏輯了
應(yīng)用啟動(dòng)后,SystemServer 會(huì)記錄從 startActivity 被調(diào)用到應(yīng)用第一幀顯示的時(shí)長(zhǎng),在 Systrace 中的顯示如下(注意結(jié)尾是應(yīng)用第一幀,如果應(yīng)用啟動(dòng)的時(shí)候是 SplashActivity -> MainActivity,那么這里的結(jié)尾只是 SplashActivity,MainActivity 的完全啟動(dòng)需要自己查看)
2.5 應(yīng)用進(jìn)程啟動(dòng)階段
通常的大型應(yīng)用,App 冷啟動(dòng)通常包括下面三個(gè)部分,每一個(gè)部分耗時(shí)都會(huì)導(dǎo)致應(yīng)用的整體啟動(dòng)速度變慢,所以在優(yōu)化啟動(dòng)速度的時(shí)候,需要明確知道應(yīng)用啟動(dòng)結(jié)束的點(diǎn)(需要跟測(cè)試溝通清楚,一般是界面保持穩(wěn)定的那個(gè)點(diǎn))
-
應(yīng)用進(jìn)程啟動(dòng)到 SplashActivity 第一幀顯示(部分 App 沒(méi)有 SplashActivity,所以可以省略這一步,直接到進(jìn)程啟動(dòng)到 主 Activit 第一幀顯示 )
-
SplashActivity 第一幀顯示到主 Activity 第一幀顯示
-
主 Activity 第一幀顯示到界面完全顯示
下面針對(duì)這三個(gè)階段來(lái)具體分析(當(dāng)然你的 App 如果簡(jiǎn)單的話,可能沒(méi)有 SplashActivity ,直接進(jìn)的就是主 Activity,那么忽略第二步就可以了)
應(yīng)用進(jìn)程啟動(dòng)到 SplashActivity 第一幀顯示
由于是冷啟動(dòng),所以 App 進(jìn)程在 Fork 之后,需要首先執(zhí)行 bindApplication ,這個(gè)也是區(qū)分冷熱啟動(dòng)的一個(gè)重要的點(diǎn)。Application 的環(huán)境創(chuàng)建好之后,就開始組件的啟動(dòng)(這里是 Activity 組件,通過(guò) Service、Broadcast、ContentProvider 組件啟動(dòng)的進(jìn)程則會(huì)在 bindApplication 之后先啟動(dòng)這些組件)
Activity 的生命周期函數(shù)會(huì)在 Activity 組件創(chuàng)建的時(shí)候執(zhí)行,包括 onStart、onCreate、onResume 等,然后還要經(jīng)過(guò)一次 Choreographer#doFrame 的執(zhí)行(包括 measure、layout、draw)以及 RenderThread 的初始化和第一幀任務(wù)的繪制,再加上 SurfaceFlinger 一個(gè) Vsync 周期的合成,應(yīng)用第一幀才會(huì)真正顯示(也就是下圖中 finishDrawing 的位置),這部分詳細(xì)的流程可以查看 Android Systrace 基礎(chǔ)知識(shí) - MainThread 和 RenderThread 解讀[7]
SplashActivity 第一幀顯示到主 Activity 第一幀顯示
大部分的 App 都有 SplashActivity 來(lái)播放廣告,播放完成之后才是真正的主 Activity 的啟動(dòng),同樣包括 Activity 組件的創(chuàng)建,包括 onStart、onCreate、onResume 、自有啟動(dòng)邏輯的執(zhí)行、WebView 的初始化等等等等,直到主 Activity 的第一幀顯示
主 Activity 第一幀顯示到界面完全加載并顯示
一般來(lái)說(shuō),主 Activity 需要多幀才能顯示完全,因?yàn)橛泻芏噘Y源(最常見(jiàn)的是圖片)是異步加載的,第一幀可能只加載了一個(gè)顯示框架、而其中的內(nèi)容在準(zhǔn)備好之后才會(huì)顯示出來(lái)。這里也可以看到,通過(guò) Systrace 不是很方便來(lái)判斷應(yīng)用冷啟動(dòng)的終點(diǎn)(除非你跟測(cè)試約定好,在某個(gè) View 顯示之后就算啟動(dòng)完成,然后你在這個(gè) View 里面打個(gè) Systrace 的 Tag,通過(guò)跟蹤這個(gè) Tag 就可以粗略判斷具體 Systrace 里面哪一幀是啟動(dòng)完成的點(diǎn))
我制作了一個(gè) Systrace 截圖的方式,來(lái)進(jìn)行演示,方便你了解 App 啟動(dòng)各個(gè)階段都對(duì)應(yīng)在 Systrace 的哪里(使用的是一個(gè)開源的 WanAndroid 客戶端)
App 啟動(dòng)圖,結(jié)合了 Systrace 和 屏幕截圖
End
本文重點(diǎn)放在了如何在 Systrace 中展示 App 的完整冷啟動(dòng)流程,方便大家在做 App 的啟動(dòng)優(yōu)化的時(shí)候,可以通過(guò) Systrace 來(lái)快速定位到啟動(dòng)瓶頸,也方便進(jìn)行競(jìng)品的對(duì)比和分析,
-
至于如何分析,可以查看 Systrace 響應(yīng)速度實(shí)戰(zhàn) 1 :了解響應(yīng)速度原理[8] 的分析套路部分
-
至于如何優(yōu)化,可以查看 Android App 啟動(dòng)優(yōu)化全記錄[9] 這篇文章,這里就不再重復(fù)了。不過(guò)隨著技術(shù)的發(fā)展,有些優(yōu)化手段會(huì)消失,而會(huì)有新的優(yōu)化手段冒出來(lái),我也會(huì)對(duì)這篇文章進(jìn)行維護(hù),如果大家發(fā)現(xiàn)新的優(yōu)化技術(shù),麻煩博客留言或者加微信(553000664)通知我,我來(lái)進(jìn)行調(diào)研和更新