原創(chuàng)文章顷帖,轉(zhuǎn)載注明出處,多謝合作鼎文。
本文從systrace的角度通過關(guān)鍵標(biāo)簽渔肩,來簡(jiǎn)單看下app冷啟動(dòng)牽扯到的圖形渲染的整個(gè)流程。代碼參考Android 9.0漂问。
以優(yōu)酷app冷啟動(dòng)為例:
先看第一幀顯示過程赖瞒,如下圖所示:
整個(gè)過程包括:
- 應(yīng)用程序啟動(dòng)女揭。
- 首幀的繪制與渲染蚤假。
- 首幀的合成與送顯。
注:
我們可以看到systrace中吧兔,每一幀主要分紅磷仰、黃、綠三種顏色:
- 紅色: 代表從performTraversals到renderthread繪制完成的時(shí)間超過2 * vsync境蔼,定義成terrible frame灶平。
- 黃色: 代表時(shí)間超過1 * vsync 不到 2* vsync,在有renderthread的情況下箍土,超過一個(gè)vsync是不一定會(huì)導(dǎo)致掉幀的逢享,所以只是黃色,定義成bad frame吴藻。
- 綠色: 代表時(shí)間在1 * vsync以內(nèi), 定義成ok frame瞒爬。
先看第一個(gè)過程:應(yīng)用程序啟動(dòng)。
因?yàn)檎归_的圖太長(zhǎng)了沟堡,這個(gè)過程又主要拆分為如下兩個(gè)階段:
1)進(jìn)程創(chuàng)建階段
Zygote初始化過程中侧但,最后會(huì)調(diào)用runSelectLoop(),隨時(shí)待命航罗,當(dāng)接收到請(qǐng)求創(chuàng)建新進(jìn)程請(qǐng)求時(shí)立即喚醒并執(zhí)行相應(yīng)工作禀横。
那么在App冷啟過程中,先確定調(diào)用的Activity以及對(duì)信息與權(quán)限的驗(yàn)證粥血,并調(diào)整其task和stack之后柏锄,會(huì)開始準(zhǔn)備創(chuàng)建進(jìn)程。
runSelectLoop之前流程是將啟動(dòng)參數(shù)封裝成ZygoteState复亏。Zygote 待命的loop通過socket獲取到請(qǐng)求信息趾娃,runOnce就開始讀取參數(shù)列表, 并執(zhí)行forkAndSpecialize來創(chuàng)建進(jìn)程,之后就是創(chuàng)建Binder線程池以及通過反射調(diào)用ActivityThread的main函數(shù)等等蜓耻。PostFork過程就在forkAndSpecialize與handleChildProc之間茫舶,描述進(jìn)程fork的過程。而ZygoteInit部分則是描述進(jìn)程啟動(dòng)之后一些準(zhǔn)備工作刹淌。
以PostFork為例:
frameworks/base/core/java/com/android/internal/os/Zygote.java
133 public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
134 int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
135 int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
...
143 if (pid == 0) {
144 Trace.setTracingEnabled(true, runtimeFlags);
146 // Note that this event ends at the end of handleChildProc,
147 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
148 }
149 VM_HOOKS.postForkCommon();
150 return pid;
151 }
從這里看到了traceBegin的標(biāo)簽饶氏,而且通過注釋我們知道對(duì)應(yīng)的End標(biāo)簽在handleChildProc.
frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java
844 private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
845 FileDescriptor pipeFd, boolean isZygote) {
...
871 // End of the postFork event.
872 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
...
}
另外讥耗,CPU調(diào)度也提一下,也是systrace需要重點(diǎn)關(guān)注的部分:
這部分表示CPU調(diào)度狀態(tài):
- 灰色:Sleeping
- 藍(lán)色:Runnable (它可以運(yùn)行疹启,但是需要等待調(diào)度程序喚醒)
- 綠色:Running
- 橙色:Uninterruptible sleep 由于 I/O 負(fù)載而不可中斷休眠
如果當(dāng)前部分存在耗時(shí)情況古程,先對(duì)比看看相同部分的Running 時(shí)間是否差不多,如果差很多可能是方法本身耗時(shí)了喊崖,如果差不多挣磨,那么可能是調(diào)度上耗時(shí)了,比如Uninterruptible sleep狀態(tài)太多荤懂,如果是這種情況的話則需要看緊隨其后的Runnable茁裙,跟一下wake up它的對(duì)應(yīng)進(jìn)程or線程是誰,一步步分析下去节仿,找到root issue晤锥。
2)Activity啟動(dòng)階段
進(jìn)程創(chuàng)建后,繼續(xù)Activity的啟動(dòng)階段廊宪,包括對(duì)要啟動(dòng)的Activity信息矾瘾、權(quán)限等的驗(yàn)證,數(shù)據(jù)的封裝箭启、任務(wù)棧的調(diào)整等等一系列操作壕翩,最后進(jìn)入應(yīng)用的ActivityThread。
ActivityThreadMain部分:
frameworks/base/core/java/android/app/ActivityThread.java
6764 public static void main(String[] args) {
6765 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
6782 ...
6783 Looper.prepareMainLooper();
...
6796 ActivityThread thread = new ActivityThread();
6797 thread.attach(false, startSeq);
6808 // End of event ActivityThreadMain.
6809 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
6810 Looper.loop();
6811
6812 throw new RuntimeException("Main thread loop unexpectedly exited");
6813 }
ActivityThreadMain這就是反映了ActivityThread的main方法執(zhí)行過程傅寡,這個(gè)過程很簡(jiǎn)單就是創(chuàng)建ActivityThread并讓它的Looper消息泵循環(huán)跑起來放妈。所以一般這個(gè)過程不會(huì)有什么耗時(shí)問題。
其中有個(gè)不起眼的方法:attach 看看它的調(diào)用流程:
ActivityThread.attach() ->AMS.attachApplicationLocked()->ActivityThread.bindApplication - >sendMessage(H.BIND_APPLICATION, data);
那么就到了接下的bindApplication過程了赏僧。
bindApplication部分:
class H extends Handler {
1665 public void handleMessage(Message msg) {
1666 if (DEBUG_MESSAGES) Slog.v(TAG, ">>> handling: " + codeToString(msg.what));
1667 // MIUI ADD
1668 long startTime = SystemClock.uptimeMillis();
1669 switch (msg.what) {
1670 case BIND_APPLICATION:
1671 Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "bindApplication");
1672 AppBindData data = (AppBindData)msg.obj;
1673 handleBindApplication(data);
1674 Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
1675 break;
...
}
}
這個(gè)標(biāo)簽對(duì)應(yīng)的就是 handleBindApplication過程大猛,應(yīng)用進(jìn)程的創(chuàng)建是從Zygote fork的進(jìn)程,copy了一份Zygote進(jìn)程的內(nèi)存拷貝淀零,在當(dāng)前過程中挽绩,就是正式往fork出來的進(jìn)程中填充屬于當(dāng)前應(yīng)用私有的資源與數(shù)據(jù)。
注:
Zygote 剛fork出應(yīng)用進(jìn)程時(shí)驾中,兩者是共享物理內(nèi)存的唉堪,且對(duì)data區(qū)內(nèi)容只有可讀權(quán)限,一方開始了寫操作了肩民,即會(huì)開辟一塊新的內(nèi)存唠亚,保存新的修改內(nèi)容。這技術(shù)就是fork copy-on-write.
bindApplication這個(gè)過程包括:
- 加載應(yīng)用資源持痰、load Class到內(nèi)存灶搜。
- 創(chuàng)建上下文。
- 初始化Instrumentation,并通過它調(diào)用application的onCreate割卖。Instrumentation是ActivityThread與組件之間的傳話官前酿,ActivityThread通過H分發(fā)出來的對(duì)應(yīng)的操作都是通過Instrumentation來調(diào)用組件實(shí)現(xiàn)。
注:
google原生邏輯是安裝應(yīng)用的時(shí)候解壓APK包鹏溯,對(duì)dex做初步優(yōu)化罢维,你會(huì)發(fā)現(xiàn)在安裝完應(yīng)用后 data/app/<packageName>/oat/arm / 路徑下會(huì)生成 .odex .vdex文件。之后會(huì)在空閑狀態(tài)下執(zhí)行dex2oat丙挽。這部分之后開章節(jié)來詳細(xì)說肺孵。
繼續(xù):
這部分就是走scheduleLaunchActivity流程了,內(nèi)容包括:
創(chuàng)建Activity颜阐,并走對(duì)應(yīng)的生命周期平窘。這個(gè)過程主要是應(yīng)用布局的加載:
setContentView 創(chuàng)建DecorView,并把xml的View樹解析出來瞬浓,加到DecorView上的contentParent部分初婆。之后Activity 調(diào)用makeVisible 通過WindowManagerGlobal執(zhí)行addView操作蓬坡,開啟下一個(gè)階段的繪制工作猿棉。
至此,第一個(gè)Vsync信號(hào)響應(yīng)階段就簡(jiǎn)單介紹完了屑咳,中間牽扯到的其他標(biāo)簽有興趣的可以一一追源碼看下萨赁,這里就不贅述了。下一篇分析首幀繪制與渲染過程兆龙。