天天看點

android R版本應用程式啟動過程源碼分析

        應用啟動方式包括四種:Activity、Broadcast、Service、Provider四種安卓元件被喚醒和拉起時都可以造成程序啟動。下面先主要以Activity啟動為例來做個簡要分析。

一、冷啟動跳轉新應用

1、 startActivity階段       

        常見的Activity啟動一般由startActivity發起,以桌面點選圖庫冷起為例,startActivity函數執行過程如下(冷啟動執行reusedTask為空分支): 

android R版本應用程式啟動過程源碼分析

08-24 20:58:18.744  1982  7944 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.wtf.gallery3d/.app.MainActivity bnds=[48,1681][294,1992] (has extras)} from uid 10100

startActivity執行到系統側時一般第一行日志輸出的關鍵字是“START u”,u0代表要啟動的頁面是主使用者0的,子使用者就是u10、u11這種(子使用者從10開始計算),該行日志還會列印發起請求的intent資訊,表明所啟動的元件目标,from uid代表發起startActivity的發起者的uid,可以根據指令adb shell ps -A|findstr u0_a100查詢到uid是10100的程序是哪個,10100要轉成u0_a100,a代表10000,如果是子使用者程序就是u10_a100這種一次類推。

在列印START u日志之後,ATMS這塊會查找複用棧,由于冷起,是以沒有複用棧,此時會建立ActivityRecord、ActivityStack(R版本開始全屏、懸浮窗等非分屏場景的ActivityStack就是作為Task在使用,二者是同一個對象,ActivityStack繼承Task),這一點從以下dump資訊也可以看出來:

android R版本應用程式啟動過程源碼分析

建立完ActivityStack對象後會列印以下events日志,該日志表面棧id是3405:

08-24 20:58:18.759  1982  7944 I wm_stack_created: 3405

一個ActivityStack在構造時就會加到棧頂,此時并沒有wm_focused_stack的events日志列印,因為建立完成後加到棧頂時傳入的updateLastFocusedStackReason是null,不會造成events日志輸出,而且即便緊接着還有一次moveToFront但由于棧頂對象還是剛才新建立的stack,是以也不會列印該events日志。

android R版本應用程式啟動過程源碼分析

在将ActivityStack作為一個Task對象後,會列印以下events日志:

08-24 20:58:18.763  1982  7944 I wm_create_task: [0,3405]

注意,雖然前面很早就建立了ActivityRecord,但是wm_create_activity這個events日志并不是構造ActivityRecord時列印的,而是在wm_create_task之後才列印出來:

08-24 20:58:18.763  1982  7944 I wm_create_activity: [0,14228247,3405,com.wtf.gallery3d/.app.MainActivity,android.intent.action.MAIN,NULL,NULL,270532608]

在建立好各級資料結構并關聯好上下級關系之後,才會真正去嘗試resumeTopActivity去resume棧頂的ActivityRecord(此時棧頂就是要啟動的圖庫應用),注意這個地方是嘗試去resume,不代表真正能去resume成功,因為此時會判斷是否有需要執行不可見的ActivityRecord要被pause,從桌面點選啟動圖庫,顯然需要把桌面pause掉,此時邏輯代碼如下:

android R版本應用程式啟動過程源碼分析

該處有兩個pause嘗試,pauseBackStacks代表要去把display上的其他棧嘗試計算可見性判别是否需要pause,比如桌面點選圖庫跳轉不同棧時,就需要執行這一步把桌面棧pause掉;而緊接着的startPausingLocked判斷條件要求目前要resume的stack具有mResumedActivity,也就是說目前棧在前台可見了,隻是棧内部有activity跳轉,也就是說應用内部進行跳轉執行這個pause。這兩個pause動作無論執行哪一個都會有以下日志輸出,當然這個僅僅是系統側的分發pause的日志,還沒到app側執行onPause:

08-24 20:58:18.765  1982  7944 I wm_pause_activity: [0,225436362,com.wtf.launcher/.Launcher,userLeaving=true]

在發起對上一個應用也就是桌面的pause操作時,判斷要resume的next也就是圖庫頁面是沒有程序存在的,需要新啟動一下程序,此時會異步執行圖庫的程序拉起,至此startActivity函數就執行完畢了,其實可以看出來,startActivity此時執行了個寂寞,僅僅發起了對上一個應用的pause(異步,所有的生命周期排程到應用側的都是oneway異步不等待的,參見IApplicationThread類帶有oneway标記)和啟動要拉起的頁面的程序(異步),除此之外,别無他物。

android R版本應用程式啟動過程源碼分析

剛才說到因為要啟動圖庫,是以要把桌面棧pause,那麼異步分發到桌面棧的程序ActivityThread上的pause消息會根據桌面主線程(四大元件的生命周期排程基本都是在主線程執行的,廣播注冊時使用了獨立looper的handler時則是例外場景)的消息排程得到執行,最終執行完onPause生命周期,這個過程已經跟圖庫那邊的程序啟動沒有關聯了,除非桌面在onPause階段執行了一些系統服務比如對ams的請求,才有可能造成跟圖庫程序啟動競争ams鎖。此時會有桌面的pause日志,且該日志代表onPause執行完畢,日志如下:

08-24 20:58:18.778 11190 11190 I wm_on_paused_called: [225436362,com.wtf.launcher.Launcher,performPause]

2、activityPaused階段

        在執行完每一個pause動作時,被pause的activity所在應用會執行服務端atms的activityPaused告知系統已經pause完成,atms服務會接下來再次嘗試一下看看能否resumeTop成功,并把被pause完成的頁面加到stopping清單,等待後續執行stop動作。activityPaused流程較為複雜,此處針對冷起場景走的是以下途圖中“!next.attachedToProcess”分支,走到該分支會執行mStackSupervisor.startSpecificActivity,該函數是每個activity被冷起時(不等價于冷起程序,比如應用程序存在但啟動了一個棧裡不存在的新activity也叫做冷起)必經之路。該函數内部又分為兩個分支:1)程序存在且已經attachApplication完畢,執行realStartActivityLocked去執行activity的冷啟動;2)程序存在但還處于啟動過程中未執行attachApplication的場景,或者程序不存在的冷起場景,執行mService.startProcessAsync去啟動程序。走1)還是2)取決于startActivity階段發起的mService.startProcessAsync啟動程序和activityPaused的先後順序,如果startActivity階段啟動程序先完成則activityPaused排程時執行1);若startActivity階段啟動程序後完成,則activityPaused排程時執行2),此時2)屬于無用的重複執行,在執行過程中會檢測到前一次發起程序啟動的指令正在執行過程中,此時2)這次重複操作會直接傳回。桌面冷起場景就屬于執行2)的場景。在執行完2)之後,activityPaused後續動作就是把被pause完成的activity加到stopping清單中,events日志如下:

08-24 20:58:18.784  1982  7944 I wm_add_to_stopping: [0,225436362,com.wtf.launcher/.Launcher,makeInvisible]

當然,由于此時resumeTop執行的“!next.attachedToProcess”分支,不會觸發ensureActivitiesVisible流程,是以依靠的是completePaused結束的那次ensure來觸發makeInvisible并加到stopping清單。

android R版本應用程式啟動過程源碼分析

另外,可能存在有些app主線程卡頓導緻activityPaused遲遲無法通知到系統,此時系統有個500ms保底操作,即系統每次發起pause操作時最多等待對端應用程式500ms,若對端無法在500ms内執行完onPause并通知系統,則會走逾時機制強行執行activityPaused動作。

先附上activityPaused流程圖,後面介紹熱啟複用棧和頁面場景時繼續分析流程。

android R版本應用程式啟動過程源碼分析

 3、startProcess階段

         前面提到startActivity階段會觸發冷啟動的應用啟動程序,完整的startProcessAsync流程如下:

android R版本應用程式啟動過程源碼分析

啟動程序過程中系統側有幾個關鍵的events和applogcat日志,首先看events日志,在建立好ProcessRecord對象(程序此時還沒被啟動,僅僅建立了ProcessRecord,其中的pid是0),會緊接着列印以下events日志,表明馬上要向zygote發起程序啟動,啟動程序之前的準備工作已完成:

08-24 20:58:18.769  1982  2298 I am_uid_running: 10180

在列印完以上events日志後,會緊接着post一個異步消息到專門負責程序孵化的mProcStartHandler中,在該handler中向zygote發起start程序的指令,等待zygote傳回孵化的程序pid後,會輸出以下events日志和applogcat日志:

08-24 20:58:18.787  1982  2354 I am_proc_start: [0,31491,10180,com.wtf.gallery3d,pre-top-activity,{com.wtf.gallery3d/com.wtf.gallery3d.app.MainActivity}]

08-24 20:58:18.787  1982  2354 I ActivityManager: Start proc 31491:com.wtf.gallery3d/u0a180 for pre-top-activity {com.wtf.gallery3d/com.wtf.gallery3d.app.MainActivity}

至此程序孵化完畢,但是僅僅是表明程序的pid産生,ProcessRecord對象還未關聯到應用端的ActivityThread。

在zygote孵化出程序時,被啟動的應用程序的ActivityThread會執行main函數,流程如下:

android R版本應用程式啟動過程源碼分析

被拉起的圖庫程序在執行main函數時會通知到ams,并把應用程序的IApplicationThread傳遞給ams去關聯綁定到ProcessRecord,作為後續系統到應用ActivityThread的aidl通信的媒介,關聯綁定完成時會輸出以下events日志:

08-24 20:58:18.810  1982  7944 I am_proc_bound: [0,31491,com.wtf.gallery3d]

在執行完綁定IApplicationThread到ProcessRecord後,系統側會緊接着執行IApplicationThread.bindApplication通知圖庫的ActivityThread去執行綁定Application的動作(oneway非阻塞式通知),然後繼續排程ATMS去執行圖庫activity的真正啟動realStartActivityLocked。應用端bindApplication也是post消息到ActvityThread的主線程(前面講過生命周期排程全是post消息到主線程去依次執行),消息處理時會依次執行:建立Application對象,建立Provider對象,執行provider的onCreate,執行Application的onCreate。當然provider裝載還要通知ams側進行記錄provider,便于其他應用查找和使用該Provider,此處暫不贅述。

4、realStartActivity階段

        在執行完startActivity、pause完上一個activity、目标activity所在程序啟動完畢等步驟之後,atms要動真格得了,會執行realStartActivityLocked去把之前startActivity階段放到棧頂的圖庫ActivityRecord執行拉起操作,具體流程圖如下:

android R版本應用程式啟動過程源碼分析

冷起一個Activity時,會輸出以下events日志:

08-24 20:58:18.815  1982  7944 I wm_restart_activity: [0,14228247,3405,com.wtf.gallery3d/.app.MainActivity]

此處有個比較容易混淆的概念,activity的冷啟動(非複用activity場景)系統側會有wm_restart_activity的events日志,但是應用端卻不會執行onRestart;隻有背景stop過的的activity重新到前台resume的熱啟動場景才會有應用端的onRestart,但是該熱啟在系統側有沒有wm_restart_activity的過程。

輸出wm_restart_activity日志後,系統側會建立LaunchActivityItem和ResumeActivityItem異步通知到圖庫所在程序ActivityThread,讓應用程序執行onCreate、onStart、onResume生命周期(系統側其實隻有Launch和Resume兩個item,這兩個代表onCreate和onResume,但是由于生命周期排程有個循環過程,必須經曆onStart,是以會補齊執行onStart,詳見TransactionExecutorHelper.getLifecyclePath函數邏輯),再發送完異步消息給ActivityThread後,系統側在設定完ActivityRecord的resume狀态後(注意此時僅僅是系統側的ActivityRecord的狀态變成了resume,并不代表應用端真正執行到了resume階段)會列印輸出以下events日志,冷起頁面場景wm_set_resumed_activity的原因是minimalResumeActivityLocked:

08-24 20:58:18.818  1982  7944 I wm_set_resumed_activity: [0,com.wtf.gallery3d/.app.MainActivity,minimalResumeActivityLocked]

系統側分發異步的LaunchActivityItem和ResumeActivityItem消息給ActivityThread後,應用端繼續在主線程中依次執行onCreate、onStart、onResume等生命周期,且輸出以下events日志:

08-24 20:58:19.013 31491 31491 I wm_on_create_called: [14228247,com.wtf.gallery3d.app.MainActivity,performCreate]

08-24 20:58:19.040 31491 31491 I wm_on_start_called: [14228247,com.wtf.gallery3d.app.MainActivity,handleStartActivity]

08-24 20:58:19.054 31491 31491 I wm_on_resume_called: [14228247,com.wtf.gallery3d.app.MainActivity,RESUME_ACTIVITY]

wm_on_xxx_called的應用端events日志都是在相應的onXxx執行完之後才會列印。

應用端執行完onResume,會通知到系統側去執行idle消息觸發之前被pause的activity執行stop。

5、activityIdle階段

        activityIdle由activity在應用端onResume完成之後在主線程bind通信到atms,其流程如下:

android R版本應用程式啟動過程源碼分析

該階段系統側會找到上一個pause完成并被加到stopping清單裡的頁面(即本文中的桌面)去真正發起stop動作,系統側會先輸出events日志表面即将異步發起stop指令給應用端ActivityThread:

08-24 20:58:19.323  1982  6072 I wm_stop_activity: [0,225436362,com.wtf.launcher/.Launcher]

應用端在主線程執行此消息,并最終執行activity的onStop函數,輸出應用端events日志:

08-24 20:58:19.331 11190 11190 I wm_on_stop_called: [225436362,com.wtf.launcher.Launcher,STOP_ACTIVITY_ITEM]

當然,部分應用(微信)的某些頁面執行完onResume是沒有activityIdle到系統側的,這種屬于app自己的hook行為,代理了ActivityThread的主線程的消息隊列,導緻無法正常通知系統側通過idle去stop上一個pause的頁面,此類異常情況由以下流程得以保證(另外系統還有個10s強制idle的機制)。

android R版本應用程式啟動過程源碼分析

至此,冷啟動一個應用程式的頁面流程分析完畢。

附完整的events日志:

08-24 20:58:18.759  1982  7944 I wm_stack_created: 3405

08-24 20:58:18.763  1982  7944 I wm_create_task: [0,3405]

08-24 20:58:18.763  1982  7944 I wm_create_activity: [0,14228247,3405,com.wtf.gallery3d/.app.MainActivity,android.intent.action.MAIN,NULL,NULL,270532608]

08-24 20:58:18.765  1982  7944 I wm_pause_activity: [0,225436362,com.wtf.launcher/.Launcher,userLeaving=true]

08-24 20:58:18.769  1982  2298 I am_uid_running: 10180

08-24 20:58:18.778 11190 11190 I wm_on_paused_called: [225436362,com.wtf.launcher.Launcher,performPause]

08-24 20:58:18.784  1982  7944 I wm_add_to_stopping: [0,225436362,com.wtf.launcher/.Launcher,makeInvisible]

08-24 20:58:18.787  1982  2354 I am_proc_start: [0,31491,10180,com.wtf.gallery3d,pre-top-activity,{com.wtf.gallery3d/com.wtf.gallery3d.app.MainActivity}]

08-24 20:58:18.810  1982  7944 I am_proc_bound: [0,31491,com.wtf.gallery3d]

08-24 20:58:18.815  1982  7944 I wm_restart_activity: [0,14228247,3405,com.wtf.gallery3d/.app.MainActivity]

08-24 20:58:18.818  1982  7944 I wm_set_resumed_activity: [0,com.wtf.gallery3d/.app.MainActivity,minimalResumeActivityLocked]

08-24 20:58:19.013 31491 31491 I wm_on_create_called: [14228247,com.wtf.gallery3d.app.MainActivity,performCreate]

08-24 20:58:19.040 31491 31491 I wm_on_start_called: [14228247,com.wtf.gallery3d.app.MainActivity,handleStartActivity]

08-24 20:58:19.054 31491 31491 I wm_on_resume_called: [14228247,com.wtf.gallery3d.app.MainActivity,RESUME_ACTIVITY]

08-24 20:58:19.323  1982  6072 I wm_stop_activity: [0,225436362,com.wtf.launcher/.Launcher]

08-24 20:58:19.331 11190 11190 I wm_on_stop_called: [225436362,com.wtf.launcher.Launcher,STOP_ACTIVITY_ITEM]

二、熱啟動跳轉新應用

1、 startActivity階段       

        仍然以桌面點選圖庫為例,不同的是此時圖庫棧本身已存在在背景,startActivity函數執行過程圖示如前圖(不同的是,熱啟動執行reusedTask非空分支)。隻要發起startActivity都會列印START u日志,無論熱起冷起。

08-24 21:03:01.022  1982  6193 I ActivityTaskManager: START u0 {act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] flg=0x10200000 cmp=com.wtf.gallery3d/.app.MainActivity bnds=[48,1681][294,1992] (has extras)} from uid 10100

在列印START u日志之後,會查找複用棧,此時找到了圖庫的複用棧,找到複用棧之後會将複用棧stack移到棧頂,輸出以下events日志:

08-24 21:03:01.023  1982  6193 I wm_focused_stack: [0,0,3405,1,positionChildAtTop]

移完棧後設定焦點app,不同于冷起activity設定焦點是在resumeTop流程,此處設定焦點的原因是bringingFoundTaskToFront:

08-24 21:03:01.025  1982  6193 I wm_set_resumed_activity: [0,com.wtf.gallery3d/.app.MainActivity,bringingFoundTaskToFront]

設定完焦點app,會繼續輸出一條task移到棧頂的日志,其實全屏棧Task和ActivityStack為同一對象,以下日志可以不用關注。

08-24 21:03:01.025  1982  6193 I wm_task_to_front: [0,3405]

執行完移棧、設定焦點動作之後,同樣需要去嘗試resumeTopActivity去resume棧頂的ActivityRecord(此時棧頂就是要啟動的圖庫應用),由于此時圖庫是複用棧熱啟拉到前台,不需要啟動程序,但是仍然需要去pause桌面,而且是跨棧pause場景:

08-24 21:03:01.026  1982  6193 I wm_pause_activity: [0,225436362,com.wtf.launcher/.Launcher,userLeaving=true]

發起pause桌面的指令後,startActivity函數執行完畢,同樣也沒有真的把圖庫resume起來,需要等待桌面的pause完成才能真正resume圖庫。

前面說到pause指令是異步通知到應用端,桌面主線程執行完此指令會輸出以下events日志:

08-24 21:03:01.037 11190 11190 I wm_on_paused_called: [225436362,com.wtf.launcher.Launcher,performPause]

2、activityPaused階段   

        桌面onPause執行完畢,同樣也是執行activityPaused通知系統atms,atms服務會再次嘗試一下看看能否resumeTop成功,此時resumeTop走的是next.attachedToProcess分支,因為是熱啟,本身棧已經啟動過,這次隻是從背景拉到前台,已經綁定程序,在該分支中先設定ActivityRecord的狀态為RESUMED,進而引發焦點app的設定,且此次設定焦點app的原因是resumeTopActivityInnerLocked:

08-24 21:03:01.041  1982  6193 I wm_set_resumed_activity: [0,com.wtf.gallery3d/.app.MainActivity,resumeTopActivityInnerLocked]

設定完焦點app,接着會執行ensureVisibilityAndConfig、ensureActivitiesVisible然後觸發将pause完成的桌面頁面加到stopping清單裡(熱啟加到stopping清單不需要依賴completePauseLocked結束那次):

08-24 21:03:01.041  1982  6193 I wm_add_to_stopping: [0,225436362,com.wtf.launcher/.Launcher,makeInvisible]

執行完ensureVisibilityAndConfig後,會列印以下events日志去準備發起resume指令到應用端,構造ResumeActivityItem觸發應用端去執行resume流程:

08-24 21:03:01.041  1982  6193 I wm_resume_activity: [0,14228247,3405,com.wtf.gallery3d/.app.MainActivity]

3、resumeActivity階段

        系統側發送resume指令到應用端,此時應用端的生命周期處于onStop,從onStop到onResume,需要補齊onRestart、onStart、onResume(詳見TransactionExecutorHelper.getLifecyclePath函數邏輯),并在執行完對應生命周期後輸出events日志:

08-24 21:03:01.075 31491 31491 I wm_on_restart_called: [14228247,com.wtf.gallery3d.app.MainActivity,performRestartActivity]

08-24 21:03:01.082 31491 31491 I wm_on_start_called: [14228247,com.wtf.gallery3d.app.MainActivity,handleStartActivity]

08-24 21:03:01.083 31491 31491 I wm_on_resume_called: [14228247,com.wtf.gallery3d.app.MainActivity,RESUME_ACTIVITY]

4、activityIdle階段

        activityIdle與前文冷起流程一模一樣,都是先系統側拿到stopping清單去發起stop指令到應用端,系統側和應用端分别輸出以下events日志:

08-24 21:03:01.313  1982  2298 I wm_stop_activity: [0,225436362,com.wtf.launcher/.Launcher]

08-24 21:03:01.328 11190 11190 I wm_on_stop_called: [225436362,com.wtf.launcher.Launcher,STOP_ACTIVITY_ITEM]

至此,熱啟動一個應用程式複用棧流程分析完畢。

附完整的events日志:

08-24 21:03:01.023  1982  6193 I wm_focused_stack: [0,0,3405,1,positionChildAtTop]

08-24 21:03:01.025  1982  6193 I wm_set_resumed_activity: [0,com.wtf.gallery3d/.app.MainActivity,bringingFoundTaskToFront]

08-24 21:03:01.025  1982  6193 I wm_task_to_front: [0,3405]

08-24 21:03:01.026  1982  6193 I wm_pause_activity: [0,225436362,com.wtf.launcher/.Launcher,userLeaving=true]

08-24 21:03:01.037 11190 11190 I wm_on_paused_called: [225436362,com.wtf.launcher.Launcher,performPause]

08-24 21:03:01.041  1982  6193 I wm_set_resumed_activity: [0,com.wtf.gallery3d/.app.MainActivity,resumeTopActivityInnerLocked]

08-24 21:03:01.041  1982  6193 I wm_add_to_stopping: [0,225436362,com.wtf.launcher/.Launcher,makeInvisible]

08-24 21:03:01.041  1982  6193 I wm_resume_activity: [0,14228247,3405,com.wtf.gallery3d/.app.MainActivity]

08-24 21:03:01.075 31491 31491 I wm_on_restart_called: [14228247,com.wtf.gallery3d.app.MainActivity,performRestartActivity]

08-24 21:03:01.082 31491 31491 I wm_on_start_called: [14228247,com.wtf.gallery3d.app.MainActivity,handleStartActivity]

08-24 21:03:01.083 31491 31491 I wm_on_resume_called: [14228247,com.wtf.gallery3d.app.MainActivity,RESUME_ACTIVITY]

08-24 21:03:01.313  1982  2298 I wm_stop_activity: [0,225436362,com.wtf.launcher/.Launcher]

08-24 21:03:01.328 11190 11190 I wm_on_stop_called: [225436362,com.wtf.launcher.Launcher,STOP_ACTIVITY_ITEM]

至此冷起和熱啟複用棧這兩個大的流程分析完畢!!!