[譯]Android Application 啟動流程分析

anly_jun發表於2016-11-16

為了便於閱讀, 應邀將Android App效能優化系列, 轉移到掘金原創上來.
掘金的新出的"收藏集"功能可以用來做系列文集了.

這是一篇關於Android Application啟動流程分析的譯文, 為我們後面講App啟動優化打個基礎.

譯者注:
原文分成兩個部分, 連結如下:
multi-core-dump.blogspot.com/2010/04/and…
multi-core-dump.blogspot.com/2010/04/and…
章節標題由譯者加註.

作者曾經在高通的Android效能組工作, 主要工作是優化Android Application的啟動時間.

1, App基礎理論

要想優化App啟動時間, 第一步就是了解App啟動程式的工作原理. 有幾個基礎理論:

Android Application與其他移動平臺有兩個重大不同點:

  1. 每個Android App都在一個獨立空間裡, 意味著其執行在一個單獨的程式中, 擁有自己的VM, 被系統分配一個唯一的user ID.
  2. Android App由很多不同元件組成, 這些元件還可以啟動其他App的元件. 因此, Android App並沒有一個類似程式入口的main()方法.

Android Application元件包括:

  • Activities: 前臺介面, 直接面向User, 提供UI和操作.
  • Services: 後臺任務.
  • Broadcast Receivers: 廣播接收者.
  • Contexnt Providers: 資料提供者.

Android程式與Linux程式一樣. 預設情況下, 每個apk執行在自己的Linux程式中. 另外, 預設一個程式裡面只有一個執行緒---主執行緒. 這個主執行緒中有一個Looper例項, 通過呼叫Looper.loop()從Message佇列裡面取出Message來做相應的處理.

那麼, 這個程式何時啟動的呢?
簡單的說, 程式在其需要的時候被啟動. 任意時候, 當使用者或者其他元件調取你的apk中的任意元件時, 如果你的apk沒有執行, 系統會為其建立一個新的程式並啟動. 通常, 這個程式會持續執行直到被系統殺死. 關鍵是: 程式是在被需要的時候才建立的.

舉個例子, 如果你點選email中的超連結, 會在瀏覽器裡面開啟一個網頁. Email App和瀏覽器App是兩個不同的App, 執行在不同的程式中. 這次點選事件促使Android系統去建立了一個新的程式來例項化瀏覽器的元件.

首先, 讓我們快速看下Android啟動流程. 與眾多基於Linux核心的系統類似, 啟動系統時, bootloader啟動核心和init程式. init程式分裂出更多名為"daemons(守護程式)"的底層的Linux程式, 諸如android debug deamon, USB deamon等. 這些守護程式處理底層硬體相關的介面.

隨後, init程式會啟動一個非常有意思的程式---"Zygote". 顧名思義, 這是一個Android平臺的非常基礎的程式. 這個程式初始化了第一個VM, 並且預載入了framework和眾多App所需要的通用資源. 然後它開啟一個Socket介面來監聽請求, 根據請求孵化出新的VM來管理新的App程式. 一旦收到新的請求, Zygote會基於自身預先載入的VM來孵化出一個新的VM建立一個新的程式.

啟動Zygote之後, init程式會啟動runtime程式. Zygote會孵化出一個超級管理程式---System Server. SystemServer會啟動所有系統核心服務, 例如Activity Manager Service, 硬體相關的Service等. 到此, 系統準備好啟動它的第一個App程式---Home程式了.

2, 啟動App流程

使用者點選Home上的一個App圖示, 啟動一個應用時:

[譯]Android Application 啟動流程分析
14719665457417

Click事件會呼叫startActivity(Intent), 會通過Binder IPC機制, 最終呼叫到ActivityManagerService. 該Service會執行如下操作:

  • 第一步通過PackageManager的resolveIntent()收集這個intent物件的指向資訊.
  • 指向資訊被儲存在一個intent物件中.
  • 下面重要的一步是通過grantUriPermissionLocked()方法來驗證使用者是否有足夠的許可權去呼叫該intent物件指向的Activity.
  • 如果有許可權, ActivityManagerService會檢查並在新的task中啟動目標activity.
  • 現在, 是時候檢查這個程式的ProcessRecord是否存在了.

如果ProcessRecord是null, ActivityManagerService會建立新的程式來例項化目標activity.

2.1 建立程式

ActivityManagerService呼叫startProcessLocked()方法來建立新的程式, 該方法會通過前面講到的socket通道傳遞引數給Zygote程式. Zygote孵化自身, 並呼叫ZygoteInit.main()方法來例項化ActivityThread物件並最終返回新程式的pid.

ActivityThread隨後依次呼叫Looper.prepareLoop()和Looper.loop()來開啟訊息迴圈.

流程圖如下:

[譯]Android Application 啟動流程分析
14719665867210

2.2 繫結Application

接下來要做的就是將程式和指定的Application繫結起來. 這個是通過上節的ActivityThread物件中呼叫bindApplication()方法完成的. 該方法傳送一個BIND_APPLICATION的訊息到訊息佇列中, 最終通過handleBindApplication()方法處理該訊息. 然後呼叫makeApplication()方法來載入App的classes到記憶體中.

流程如下:

[譯]Android Application 啟動流程分析
14719665679990

2.3 啟動Activity

經過前兩個步驟之後, 系統已經擁有了該application的程式. 後面的呼叫順序就是普通的從一個已經存在的程式中啟動一個新程式的activity了.

實際呼叫方法是realStartActivity(), 它會呼叫application執行緒物件中的sheduleLaunchActivity()傳送一個LAUNCH_ACTIVITY訊息到訊息佇列中, 通過 handleLaunchActivity()來處理該訊息.

假設點選的是一個視訊瀏覽的App, 其流程如下:

[譯]Android Application 啟動流程分析
14719666088428

相關文章