譯者注:
原文分成兩個(gè)部分, 鏈接如下:
http://multi-core-dump.blogspot.com/2010/04/android-application-launch.html
http://multi-core-dump.blogspot.com/2010/04/android-application-launch-part-2.html
本文合二為一, 章節(jié)標(biāo)題由譯者加注.
作者曾經(jīng)在高通的Android性能組工作, 主要工作是優(yōu)化Android Application的啟動(dòng)時(shí)間.
1, App基礎(chǔ)理論
要想優(yōu)化App啟動(dòng)時(shí)間, 第一步就是了解App啟動(dòng)進(jìn)程的工作原理. 有幾個(gè)基礎(chǔ)理論:
Android Application與其他移動(dòng)平臺(tái)有兩個(gè)重大不同點(diǎn):
- 每個(gè)Android App都在一個(gè)獨(dú)立空間里, 意味著其運(yùn)行在一個(gè)單獨(dú)的進(jìn)程中, 擁有自己的VM, 被系統(tǒng)分配一個(gè)唯一的user ID.
- Android App由很多不同組件組成, 這些組件還可以啟動(dòng)其他App的組件. 因此, Android App并沒有一個(gè)類似程序入口的main()方法.
Android Application組件包括:
- Activities: 前臺(tái)界面, 直接面向User, 提供UI和操作.
- Services: 后臺(tái)任務(wù).
- Broadcast Receivers: 廣播接收者.
- Contexnt Providers: 數(shù)據(jù)提供者.
Android進(jìn)程與Linux進(jìn)程一樣. 默認(rèn)情況下, 每個(gè)apk運(yùn)行在自己的Linux進(jìn)程中. 另外, 默認(rèn)一個(gè)進(jìn)程里面只有一個(gè)線程---主線程. 這個(gè)主線程中有一個(gè)Looper實(shí)例, 通過調(diào)用Looper.loop()從Message隊(duì)列里面取出Message來做相應(yīng)的處理.
那么, 這個(gè)進(jìn)程何時(shí)啟動(dòng)的呢?
簡單的說, 進(jìn)程在其需要的時(shí)候被啟動(dòng). 任意時(shí)候, 當(dāng)用戶或者其他組件調(diào)取你的apk中的任意組件時(shí), 如果你的apk沒有運(yùn)行, 系統(tǒng)會(huì)為其創(chuàng)建一個(gè)新的進(jìn)程并啟動(dòng). 通常, 這個(gè)進(jìn)程會(huì)持續(xù)運(yùn)行直到被系統(tǒng)殺死. 關(guān)鍵是: 進(jìn)程是在被需要的時(shí)候才創(chuàng)建的.
舉個(gè)例子, 如果你點(diǎn)擊email中的超鏈接, 會(huì)在瀏覽器里面打開一個(gè)網(wǎng)頁. Email App和瀏覽器App是兩個(gè)不同的App, 運(yùn)行在不同的進(jìn)程中. 這次點(diǎn)擊事件促使Android系統(tǒng)去創(chuàng)建了一個(gè)新的進(jìn)程來實(shí)例化瀏覽器的組件.
首先, 讓我們快速看下Android啟動(dòng)流程. 與眾多基于Linux內(nèi)核的系統(tǒng)類似, 啟動(dòng)系統(tǒng)時(shí), bootloader啟動(dòng)內(nèi)核和init進(jìn)程. init進(jìn)程分裂出更多名為"daemons(守護(hù)進(jìn)程)"的底層的Linux進(jìn)程, 諸如android debug deamon, USB deamon等. 這些守護(hù)進(jìn)程處理底層硬件相關(guān)的接口.
隨后, init進(jìn)程會(huì)啟動(dòng)一個(gè)非常有意思的進(jìn)程---"Zygote". 顧名思義, 這是一個(gè)Android平臺(tái)的非?;A(chǔ)的進(jìn)程. 這個(gè)進(jìn)程初始化了第一個(gè)VM, 并且預(yù)加載了framework和眾多App所需要的通用資源. 然后它開啟一個(gè)Socket接口來監(jiān)聽請求, 根據(jù)請求孵化出新的VM來管理新的App進(jìn)程. 一旦收到新的請求, Zygote會(huì)基于自身預(yù)先加載的VM來孵化出一個(gè)新的VM創(chuàng)建一個(gè)新的進(jìn)程.
啟動(dòng)Zygote之后, init進(jìn)程會(huì)啟動(dòng)runtime進(jìn)程. Zygote會(huì)孵化出一個(gè)超級(jí)管理進(jìn)程---System Server. SystemServer會(huì)啟動(dòng)所有系統(tǒng)核心服務(wù), 例如Activity Manager Service, 硬件相關(guān)的Service等. 到此, 系統(tǒng)準(zhǔn)備好啟動(dòng)它的第一個(gè)App進(jìn)程---Home進(jìn)程了.
2, 啟動(dòng)App流程
用戶點(diǎn)擊Home上的一個(gè)App圖標(biāo), 啟動(dòng)一個(gè)應(yīng)用時(shí):

Click事件會(huì)調(diào)用startActivity(Intent), 會(huì)通過Binder IPC機(jī)制, 最終調(diào)用到ActivityManagerService. 該Service會(huì)執(zhí)行如下操作:
- 第一步通過PackageManager的resolveIntent()收集這個(gè)intent對象的指向信息.
- 指向信息被存儲(chǔ)在一個(gè)intent對象中.
- 下面重要的一步是通過grantUriPermissionLocked()方法來驗(yàn)證用戶是否有足夠的權(quán)限去調(diào)用該intent對象指向的Activity.
- 如果有權(quán)限, ActivityManagerService會(huì)檢查并在新的task中啟動(dòng)目標(biāo)activity.
- 現(xiàn)在, 是時(shí)候檢查這個(gè)進(jìn)程的ProcessRecord是否存在了.
如果ProcessRecord是null, ActivityManagerService會(huì)創(chuàng)建新的進(jìn)程來實(shí)例化目標(biāo)activity.
2.1 創(chuàng)建進(jìn)程
ActivityManagerService調(diào)用startProcessLocked()方法來創(chuàng)建新的進(jìn)程, 該方法會(huì)通過前面講到的socket通道傳遞參數(shù)給Zygote進(jìn)程. Zygote孵化自身, 并調(diào)用ZygoteInit.main()方法來實(shí)例化ActivityThread對象并最終返回新進(jìn)程的pid.
ActivityThread隨后依次調(diào)用Looper.prepareLoop()和Looper.loop()來開啟消息循環(huán).
流程圖如下:

2.2 綁定Application
接下來要做的就是將進(jìn)程和指定的Application綁定起來. 這個(gè)是通過上節(jié)的ActivityThread對象中調(diào)用bindApplication()方法完成的. 該方法發(fā)送一個(gè)BIND_APPLICATION的消息到消息隊(duì)列中, 最終通過handleBindApplication()方法處理該消息. 然后調(diào)用makeApplication()方法來加載App的classes到內(nèi)存中.
流程如下:

2.3 啟動(dòng)Activity
經(jīng)過前兩個(gè)步驟之后, 系統(tǒng)已經(jīng)擁有了該application的進(jìn)程. 后面的調(diào)用順序就是普通的從一個(gè)已經(jīng)存在的進(jìn)程中啟動(dòng)一個(gè)新進(jìn)程的activity了.
實(shí)際調(diào)用方法是realStartActivity(), 它會(huì)調(diào)用application線程對象中的sheduleLaunchActivity()發(fā)送一個(gè)LAUNCH_ACTIVITY消息到消息隊(duì)列中, 通過 handleLaunchActivity()來處理該消息.
假設(shè)點(diǎn)擊的是一個(gè)視頻瀏覽的App, 其流程如下:
