Android App 啟動原理
說白了,App 啟動原理就是在講一件事:
你點了桌面圖標之后,App 到底是怎么一步步跑起來并把頁面顯示出來的。
這里面會出現(xiàn)幾個關鍵角色:
Launcher(桌面 App)
AMS(ActivityManagerService)
Zygote
應用進程創(chuàng)建
ActivityThread
Application
目標Activity
先記住這條主線就夠了:
點圖標 -> Launcher 發(fā)起啟動 -> AMS 校驗+調(diào)度 -> Zygote fork 進程 -> 應用進程起來 -> 創(chuàng)建 Application -> 啟動 Activity -> 頁面顯示
先搞清楚什么叫“App 啟動”
平時說啟動,一般分 3 種:
冷啟動:進程沒了,需要新建進程
熱啟動:進程還在,只是把界面切回前臺
溫啟動:進程在,但Activity要重建
面試最愛問的是冷啟動,因為它把系統(tǒng)關鍵鏈路都串起來了。
點圖標后,第一步是誰在處理
你點圖標時,先響應的是Launcher(桌面程序)。
Launcher 本質(zhì)也是普通 Android 應用,只是它負責桌面和應用入口。
它會拿到目標 App 的Intent,然后發(fā)起startActivity()請求。
但這時候還不會直接進目標 App,而是先交給系統(tǒng)服務處理。
簡單說就是:
Launcher 不負責創(chuàng)建目標進程
真正做啟動調(diào)度的是系統(tǒng)服務
這個核心服務就是AMS
AMS 在這個過程里干什么
AMS(ActivityManagerService)是 Android 里負責組件調(diào)度、進程管理的核心系統(tǒng)服務。
收到 Launcher 請求后,它主要做這幾件事:
檢查目標Activity能不能啟動
看目標 App 進程在不在
不在就先創(chuàng)建進程
進程好了再通知應用啟動目標Activity
所以可以把 AMS 理解成“總調(diào)度”:
它不畫頁面,但它決定誰先做、什么時候做、在哪個進程做。
進程不存在時,怎么創(chuàng)建
這一步是冷啟動最核心的地方。
Android 不會讓 App 自己亂建進程,而是統(tǒng)一交給Zygote來孵化。
4.1 Zygote 是什么
Zygote可以理解成“應用進程母體”。
它提前做了很多通用初始化,比如:
啟動虛擬機
加載系統(tǒng)類
預加載常用資源
這樣后面創(chuàng)建應用進程會更快,而且fork還能復用父進程資源,省內(nèi)存。
4.2 AMS 怎么讓 Zygote 創(chuàng)建進程
AMS 發(fā)現(xiàn)目標 App 還沒進程,就會給Zygote發(fā)請求。
然后Zygote通過fork()拉起一個新的應用進程。
新進程起來后,會有自己獨立的:
進程空間
虛擬機環(huán)境
主線程
到這一步,App 就不是“靜態(tài)安裝包”了,而是一個真正跑起來的 Linux 進程。
新進程起來后,誰接管主線程
進程創(chuàng)建完,不是馬上就能看到頁面。
它先會進入應用自己的入口,也就是ActivityThread。
很多人看名字會誤會,以為它只是個線程類。其實它更像:
應用主線程這邊的總管。
它會負責一堆關鍵事:
建主線程消息循環(huán)
跟 AMS 通信
創(chuàng)建Application
創(chuàng)建并啟動Activity
一句話理解:
AMS 管系統(tǒng)側(cè)調(diào)度,ActivityThread管應用側(cè)執(zhí)行。
6.Application什么時候創(chuàng)建
在目標Activity真正啟動前,系統(tǒng)會先把Application創(chuàng)建出來。
常見流程大概是:
創(chuàng)建LoadedApk等運行信息
創(chuàng)建應用Context
反射創(chuàng)建Application實例
回調(diào)Application#onCreate()
所以我們才會把全局初始化放在Application#onCreate(),比如:
日志框架
路由
網(wǎng)絡庫
崩潰監(jiān)控
但這里有個重點:
Application#onCreate()越重,冷啟動通常越慢。
因為這一步?jīng)]結束,首頁一般還沒真正展示出來。
7.Activity是怎么啟動的
應用進程準備好后,AMS 會通知應用去啟動目標Activity。
應用側(cè)的ActivityThread收到消息后,會走一套啟動流程,大概包括:
創(chuàng)建Activity實例
創(chuàng)建Context
綁定PhoneWindow
調(diào)用Activity#attach()
觸發(fā)生命周期回調(diào)
常見啟動順序是:
onCreate()
onStart()
onResume()
一般在onCreate()里會做:
setContentView()
初始化 View
初始化數(shù)據(jù)
綁定監(jiān)聽
等界面測量、布局、繪制都完成,用戶才會真正看到頁面。
從源碼角度記啟動鏈路
你可以把主鏈路記成下面這 9 步:
Launcher 發(fā)起startActivity()
請求進入系統(tǒng)進程里的AMS
AMS 判斷目標進程在不在
不在就讓Zygotefork 新進程
新進程進入ActivityThread.main()
創(chuàng)建Application
ActivityThread收到啟動Activity消息
創(chuàng)建并回調(diào)目標Activity
頁面繪制完成,啟動結束
不要求你死背每個源碼細節(jié),但這 4 件事一定要清楚:
誰發(fā)起
誰調(diào)度
誰創(chuàng)建進程
誰在應用側(cè)執(zhí)行生命周期
為什么冷啟動會慢
根本原因就一句話:冷啟動要做的事太多,而且很多都堆在主線程前期。
常見耗時點有:
創(chuàng)建進程本身要時間
Application#onCreate()干太多事
首頁Activity#onCreate()邏輯太重
首屏布局太深,繪制慢
同步 I/O、數(shù)據(jù)庫、網(wǎng)絡阻塞主線程
初始化 SDK 太多
所以很多時候慢的不是框架流程本身,而是業(yè)務初始化太“重”。
啟動優(yōu)化一般在優(yōu)化什么
核心目標就兩個:
首屏更早可見,主線程更少被卡。
常見做法:
非核心組件延遲初始化
按優(yōu)先級拆初始化任務
耗時任務盡量異步
降低首頁布局復雜度
避免主線程 I/O
用啟動任務編排框架統(tǒng)一管理順序
比如:
埋點、統(tǒng)計、更新檢查可以延后
不影響首屏的 SDK 放子線程
首屏只加載必須數(shù)據(jù)
面試里怎么講更順
如果面試官讓你簡單講 App 啟動,可以這么說:
App 啟動從 Launcher 點圖標開始,Launcher 把請求交給 AMS。AMS 負責統(tǒng)一調(diào)度:先判斷目標進程是否存在,不存在就讓 Zygote fork 新進程。新進程起來后進入ActivityThread,先創(chuàng)建Application,再創(chuàng)建并啟動目標Activity,最后頁面繪制完成,用戶看到首頁。
如果對方繼續(xù)追問,你可以往這幾個點展開:
冷啟動、熱啟動、溫啟動區(qū)別
Zygote為什么能加速啟動
Application和Activity的先后順序
啟動優(yōu)化主要優(yōu)化哪些耗時點
一張簡化流程圖
整條鏈路可以記成:
Launcher -> AMS -> Zygote -> App Process -> ActivityThread -> Application -> Activity -> View 顯示
一句話收尾
App 啟動的核心就是:系統(tǒng)先決定要不要建進程,再由應用進程完成Application和Activity創(chuàng)建,最后把頁面繪制出來。
只記 3 個關鍵詞也行:
AMS:負責調(diào)度
Zygote:負責建進程
ActivityThread:負責應用側(cè)執(zhí)行啟動流程