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