Android應用啓動流程分析

1 前言

網上看過不少Activity啓動過程的源碼解析,不少文章會貼上一大段代碼,而後從startActivity()函數開始深究整個源碼的調用棧。我的感受這類文章代碼細節太多,反而容易迷失在源碼調用之中,從而忽略了Activity啓動過程的本質。因此本文就簡單地定性地對Activity啓動過程進行描述,不會貼上大篇幅的源碼,同時梳理一下相關的經典問題。也是對之前的所學作一個複習總結。html

2 冷啓動與熱啓動

Activity啓動過程當中,通常會牽涉到應用啓動的流程。應用啓動又分爲冷啓動和熱啓動。前端

  1. 冷啓動:點擊桌面圖標,手機系統不存在該應用進程,這時系統會從新fork一個子進程來加載Application並啓動Activity,這個啓動方式就是冷啓動。
  2. 熱啓動:應用的熱啓動比冷啓動簡單得多,開銷也更低。在熱啓動中,由於系統裏已有該應用的進程,因此係統的全部工做就是將您的 Activity 帶到前臺。 冷啓動是應用徹底從0開始啓動,涉及到更多的內容,因此就應用冷啓動的過程展開討論。

3 應用啓動流程

通常來講,冷啓動包括瞭如下內容:java

  1. 啓動進程 點擊圖標發生在Launcher應用的進程,startActivity()函數最終是由Instrumentation經過Android的Binder跨進程通訊機制 發送消息給 system_server 進程; 在 system_server 中,啓動進程的操做由ActivityManagerService 經過 socket 通訊告知 Zygote 進程 fork 子進程(app進程)
  2. 開啓主線程 app 進程啓動後,首先是實例化 ActivityThread,並執行其main()函數:建立 ApplicationThread,Looper,Handler 對象,並開啓主線程消息循環Looper.loop()
  3. 建立並初始化 Application和Activity ActivityThread的main()調用 ActivityThread#attach(false)方法進行 Binder 通訊,通知system_server進程執行 ActivityManagerService#attachApplication(mAppThread)方法,用於初始化Application和Activity。 在system_server進程中,ActivityManagerService#attachApplication(mAppThread)裏依次初始化了Application和Activity,分別有2個關鍵函數: - thread#bindApplication()方法通知主線程Handler 建立 Application 對象、綁定 Context 、執行 Application#onCreate() 生命週期 - mStackSupervisor#attachApplicationLocked()方法中調用 ActivityThread#ApplicationThread#scheduleLaunchActivity()方法,進而經過主線程Handler消息通知建立 Activity 對象,而後再調用 mInstrumentation#callActivityOnCreate()執行 Activity#onCreate() 生命週期
  4. 佈局&繪製 源碼流程能夠參考Android View 的繪製流程分析及其源碼調用追蹤

至此,應用啓動流程完成。linux

其中一、二、3的源碼流程能夠參考Android Application 啓動流程分析及其源碼調用探究,但代碼細節不是本篇重點。android

下面說說上述流程中的幾個關鍵角色,以及其做用:git

3.1 zygote進程

這裏稍微說下Android系統下的進程機制,每一個應用運行時都是:github

  1. 一個單獨的dalvik虛擬機(DVM) java代碼在編譯後須要運行在JVM上,一樣android中使用了java語言,也須要運行在一個VM上。因此谷歌針對手機處理器和內存等硬件資源不足而研究出來DVM,爲android運行提供環境。 參考JVM與DVM的關係
  2. 一個單獨的進程 每一個應用啓動都運行一個單獨的DVM,每一個DVM單獨佔用一個Linux進程。獨立的進程能夠防止在虛擬機崩潰的時候全部程序都被關閉。 dalvik進程管理是依賴於linux的進程體系結構的,如要爲應用程序建立一個進程,它會使用linux的fork機制來複制一個進程。

衆所周知,Android是基於Linux系統的,在Linux中全部的進程都是由init進程直接或者是間接fork出來的。fork進程每每比建立進程效率更高。在Android中,全部的應用的進程都是由zygote進程fork出來的。編程

提到zygote進程,就不得不介紹下Android開機流程:瀏覽器

  1. Android手機開機Linux內核啓動後,會加載system/core/init/init.rc文件,啓動init進程。這個進程是Android系統特有的初始化程序,簡單介紹下它的工做:安全

    • 各類複雜工做
    • 負責開關機畫面
    • 文件系統的建立和掛載
    • 啓動Zygote(孵化器)進程
    • 啓動ServiceManager,它是Binder服務管理器,管理全部Android系統服務
  2. 在系統啓動後init進程會fork Zygote進程,Zygote做爲孵化器進程,它的main函數會建立好本身的環境準備孵化子進程,並開始等待孵化請求:

    • 建立一個server端的socket, name爲zynote,用於和客戶端進程通訊
    • 預加載類和資源,提升應用啓動速度
    • 啓動SystemServer進程
    • 監聽socket,當有一個應用程序啓動時,就會向它發出請求,而後zygote進程fock本身來建立的一個新的子進程。
  3. Zygote進程首先會fork本身孵化出SystemServer進程,它的main函數主要負責:

    • 啓動binder線程池,這是SystemServer與其餘進程通訊的基礎
    • 初始化Looper
    • 建立了SystemServiceManager對象,它會啓動Android中的各類服務。包括AMS、PMS、WMS
    • 啓動桌面進程,這樣才能讓用戶見到手機的界面。
    • 開啓loop循環,開啓消息循環,SystemServer進程一直運行,保障其餘應用程序的正常運行。
  4. 當系統裏面的zygote進程運行以後,後續啓動APP,就至關於開啓一個新的進程。而Android爲了實現資源共用和更快的啓動速度,子進程都是經過zygote進程fork出來的。因此說,除了init進程fork出來的第一個進程zygote,其餘應用進程都是zygote的子進程,也不難理解爲什麼這個孵化器進程的英文名叫zygote(受精卵),由於全部的應用進程都是由它孵化誕生。

3.2 SystemServer進程

SystemServer是由zygote進程fork出來的第一個進程,SystemServer和Zygote是Android Framework最重要的2個進程。 系統裏面重要的服務都是在這個進程裏面開啓的,好比ActivityManagerService、PackageManagerService、WindowManagerService。

應用啓動流程基本是圍繞着ActivityManagerService和ActivityThread展開。

3.3 Android系統裏的Client/Server模式

平時咱們所熟知的前端(Web\Android\iOS)經過網絡與服務器通訊是客戶端-服務端模式的體現,而在Android Framework中,四大組件的建立、生命週期也是經過這樣的模式進行通訊:

  1. 服務器端(server)指的就是SystemServer進程,這個進程提供了不少服務 好比AMS、PMS、WMS等等,全部的App進程均可以與其通訊。
  2. 客戶端(client)指的就是各個獨立的App進程。

Android開發者都應該知道,經過包名和Activity類名就能夠打開一個APP。實際上,項目裏的業務代碼startActivity()方法並非直接建立進程、拉起APP的。而是經過一系列的調用,把請求傳遞給SystemServer的AMS。AMS收到來自客戶端的請求後,再通知zygote進程來fork一個新進程,來開啓咱們的目標App的。 這就像是在瀏覽器裏打開一個網頁,瀏覽器把url和參數發送到服務器,而後仍是服務器處理請求,並返回相應的html並展現在瀏覽器上。

這個過程涉及到3個進程:App進程、AMS(SystemServer進程)、zygote進程。

  1. App進程與AMS經過Binder機制進行跨進程通訊
  2. AMS(SystemServer進程)與zygote經過Socket進行跨進程通訊。

在Android系統中,任何一個Activity的啓動都是由AMS和App進程(主要是ActivityThread)相互配合來完成的。AMS服務統一調度系統中全部進程的Activity啓動,而每一個Activity的啓動過程則由其所屬的進程具體來完成。

3.4 Android Binder機制

咱們知道AMS與ActivityThread的交互主要是經過進程間通訊 (IPC) 。跨進程通訊的機制就是將方法調用及其數據分解至操做系統可識別的程度,並將其從本地進程和地址空間傳輸至遠程進程和地址空間,而後在遠程進程中從新組裝並執行該調用。 Android 提供了執行這些 IPC 事務的方案——Binder機制,所以咱們只需關心接口定義和實現 RPC 編程接口。

而App進程與SystemServer進程也是經過Binder機制進行進程間通訊,Android爲此設計了兩個Binder接口:

  1. IApplicationThread: 做爲系統進程請求應用進程的接口;
  2. IActivityManager: 做爲應用進程請求系統進程的接口。

對於一個Binder接口,在客戶端和服務端各有一個實現:Proxy和Native,它們之間的通訊主要是經過transact和onTransact觸發。通常從命名上能夠區分:xxxNative是在本進程內的Binder代理類,xxxProxy是在對方進程的Binder代理類。

多說一句,這些Binder都由ServiceManager統一管理:

  1. ServiceManager管理全部的Android系統服務,有人把ServiceManager比喻成Binder機制中的DNS服務器,client端應用若是要使用系統服務,調用getSystemService接口,ServiceManager就會經過字符串形式的Binder名稱找到並返回對應的服務的Binder對象。
  2. 它是一個系統服務進程,在native層啓動,它在system/core/rootdir/init.rc腳本中描述並由init進程啓動。
  3. ServiceManager啓動後,會經過循環等待來處理Client進程的通訊請求。

App進程與SystemServer進程的Binder接口以下圖:

出處見水印

3.4.1 服務端 IActivityManager——ActivityManagerNative——ActivityManagerService

  1. ActivityManagerNative做爲服務端的「樁(Stub)」,其主要職責就是對遠程傳遞過來的數據進行」反序列化(unparcel)」;
  2. ActivityManagerProxy做爲服務的「代理(Proxy)」,運行在客戶端,其主要職責就是將數據進行「序列化(parcel)」,再傳遞給遠程的「樁(Stub)」,App使用AMS提供的功能,好比startActivity,是經過AMS在客戶端的代理ActivityManagerProxy發起的。
  3. 最下面一層是樁(Stub)的具體實現——AMS(ActivityManagerService),負責系統中四大組件的啓動、切換、調度及應用進程的管理和調度等工做,很是複雜。

AMS是一個系統服務,在SystemServer進程啓動後完成初始化。在應用啓動流程中,充當着服務端的角色。 App中Activity的生命週期由AMS管理,它決定着何時該調用onCreate、onResume這些生命週期函數,把Activity放在哪一個棧裏,上下文之間的關係是怎樣的等等。

好比:

  1. startActivity 最終調用了AMS的 startActivity 系列方法,實現了Activity的啓動;Activity的生命週期回調,也在AMS中完成;
  2. startService,bindService 最終調用到AMS的startService和bindService方法;
  3. 動態廣播的註冊和接收在 AMS 中完成(靜態廣播在 PMS 中完成)
  4. getContentResolver 最終從 AMS 的 getContentProvider 獲取到ContentProvider

3.4.2 客戶端 IApplicationThread——ApplicationThreadNative——ActivityThread

  1. 樁(Stub):ApplicationThreadNative
  2. 代理(Proxy):ApplicationThreadProxy,App在客戶端進程中實現了實例化Activity、調用onCreate等生命週期函數的功能,由於跨進程也不能被AMS直接調用,而是AMS經過客戶端的代理ApplicationThreadProxy來處理。
  3. 最下面一層是樁(Stub)的具體實現——ApplicationThread,它是ActivityThread的一個內部類,ApplicationThread負責響應系統進程發起的請求,而實際觸發的業務邏輯是在ActivityThread中。與通常的代理模式不一樣,它不是直接持有ActivityThead的一個引用,而是把處理的請求發到ActivityThread內部的一個Handler上。

和服務端的AMS相對應,ActivityThread在應用啓動的Client/Server模式中,是做爲客戶端那一邊的具體實現。它並非一個線程,但它包含了一個應用進程的主線程運做的所有機制:

  1. 啓動應用的主線程,並開啓消息循環
  2. 提供了一個IActivityThread接口做爲與AMS的通信接口,經過該接口AMS能夠將Activity的狀態變化傳遞到客戶端的Activity對象

3.5 啓動一個Activity的通訊過程

咱們已經知道應用進程建立之後,App進程的ActivityThread與SystemServer進程的AMS經過Binder進行通訊。 在文章前面【2 應用啓動流程】提到,在ActivityThread的main方法中,調用 ActivityThread#attach(false)方法進行 Binder 通訊,通知system_server進程執行 ActivityManagerService#attachApplication(mAppThread)方法,用於初始化Application和Activity。

能夠結合源碼流程,稍微總結一下這個通訊過程。

3.5.1 Application的初始化

從應用進程到系統進程

在ActivityThread建立的時候,會將本身的ApplicationThread綁定到AMS中:

ActivityThread.main()
└── ActivityThread.attach()
    └── IActivityManager.attachApplication(mAppThread)
        └── Binder.transact()
複製代碼

應用進程做爲客戶端,經過IAcitivtyManager接口發起了跨進程調用,跨進程傳遞的參數mAppThread就是IApplicationThread的實例,執行流程從應用進程進入到系統進程:

ActivityManagerService.onTransact()
└── ActivityManagerService.attachApplication(IApplicationThread thread)
複製代碼

AMS做爲IActivityManager接口的服務端實現,會響應客戶端的請求,最終AMS.attachApplication()函數會被執行,該函數接收跨進程傳遞過來的IApplicationThread實例,將存在系統進程維護的ProcessRecord中。 具體細節能夠看AMS的源碼,咱們只須要知道AMS中維護了全部進程運行時的信息(ProcessRecord),一旦發生了應用進程的綁定請求,ProcessRecord.thread就被賦值成應用進程的IApplicationThread實例,這樣一來,在AMS中就能經過該IApplicationThread實例發起嚮應用進程的調用

從系統進程到應用進程

在AMS.attachApplication()的過程當中,會有一些信息要傳遞給應用進程,以便應用進程的初始化,系統進程會發起以下函數調用:

ActivityManagerService.attachApplication()
└── ActivityManagerService.attachApplicationLocked()
    └── IApplicationThread.bindApplication(processName, appInfo ...)
        └── Binder.transact()
複製代碼

此時,AMS會反轉角色,即系統進程做爲客戶端,經過IApplicationThread接口嚮應用進程發起調用。

  1. AMS經過ProcessRecord來維護進程運行時的狀態信息,須要將應用進程綁定到ProcessRecord才能開始一個Application的構建;
  2. AMS維護的ProcessRecord這個數據結構,包含了進程運行時的信息,譬如應用進程的名稱processName、解析AndroidManifest.xml獲得的數據結構ApplicationInfo等,其中,要傳遞給應用進程的數據都是Parcelable類型的實例。

應用進程響應請求的調用關係以下所示:

ApplicationThread.onTransact()
└── ApplicationThread.bindApplication()
    └── ActivityThread.H.handleMessage(BIND_APPLICATION)
        └── ActivityThread.handleBindApplication()
            └── Application.onCreate()
複製代碼

ApplicationThread做爲IApplicationThread接口的服務端實現,運行在應用進程中,而後ApplicationThread.bindApplication()會被執行,完成一些簡單的數據封裝(AppBindData)後,經過Handler拋出BIND_APPLICATION消息。這一拋,就拋到了主線程上,ActivityThread.handleBindApplication()會被執行,終於建立了Application 對象,而後調用 Application#attach(context) 來綁定 Context,並調用Application.onCreate()函數。歷經應用進程和系統進程之間的一個來回,總算是建立了一個應用程序。

Android源碼裏有較統一的函數命名方式,在AMS中與Activity管理相關不少函數都會帶有Locked後綴,表示這些函數的調用都須要多線程同步操做(synchronized),它們會讀/寫一些多線程共享的數據

3.5.2 Activity的初始化

前面提到在system_server進程中,ActivityManagerService#attachApplication(mAppThread)裏依次初始化了Application和Activity,其中的mStackSupervisor#attachApplicationLocked(ProcessRecord)裏進行了Activity的初始化。

  1. AMS經過ActivityRecord來維護Activity運行時的狀態信息,須要將Activity綁定到AMS中的ActivityRecord能開始Activity的生命週期。
  2. 在Activity類中有一個IBinder類型的屬性:private IBinder mToken;,IBinder類型表示這個屬性是一個遠程對象的引用,Token持有了一個ActivityRecord實例的弱引用。在建立一個ActivityRecord的時候,就會建立了一個Token類型的對象。

在啓動一個新的Activity時,AMS會將ActivityRecord的Token傳遞給應用進程,調用關係以下所示:

ActivityStackSupervisor.realStartActivityLocked(ActivityRecord, ...)
└── IApplicationThread.scheduleLaunchActivity(...token, ...)
    // 將ActivityRecord的Token跨進程傳遞給應用進程
    └── Binder.transact()
複製代碼

ActivityStackSupervisor.realStartActivityLocked()表示要啓動一個Activity實例,ActivityRecord做爲參數。從ActivityRecord中提取出Token對象,做爲跨進程調用的參數,經過IApplicationThread.scheduleLaunchActivity()傳遞到應用進程。

在應用進程這一側,會收到啓動Activity的跨進程調用,觸發如下函數的調用:

ApplicationThread.onTransact()
└── ApplicationThread.scheduleLaunchActivity(...token, ...)
    // token將被封裝進ActivityClientRecord這個數據結構中
    └── ActivityThread.H.handleMessage()
        └── ActivityThread.handleLaunchActivity(LAUNCH_ACTIVITY)
            └── ActivityThread.performLaunchActivity(ActivityClientRecord, ...)
                // 從ActivityRecord取出token
                └── Activity.attch(...token, ...)
複製代碼

標準的Binder服務端處理流程,收到AMS傳遞過來的Token對象,進行一下數據封裝(ActivityClientRecord),而後經過Handler拋出一個LAUNCH_ACTIVITY消息。這個消息顯然也是拋到了應用進程的主線程去執行,因此ActivityThread.performLaunchActivity()函數會在主線程上執行,該函數從封裝的數據結構ActivityClientRecord中取出Token對象,調用Activity.attach()函數,將其綁定到Activity上,如此一來,就創建應用進程的Activity與系統進程中ActivityRecord的關聯。

系統進程維護的是ActivityRecord,應用進程維護的是Activity,二者之間的映射關係就是利用Token來維繫的。應用進程的Activity在建立的時候,就被賦予了一個Token,拿着這個Token才能完成後續與系統進程的通訊。在發生Activity切換時,應用進程會將上一個Activity的Token(AMS.startActivity()的輸入參數resultTo)傳遞給系統進程,系統進程會根據這個Token找到ActivityRecord,對其完成調度後,再通知應用進程:Activity狀態發生了變化。

3.5.3 Instrumentation

每一個Activity都持有Instrumentation對象的一個引用,可是整個應用程序進程只會存在一個Instrumentation對象。 Instrumentation能夠理解爲應用進程的管家,ActivityThread要建立或暫停某個Activity時,都須要經過Instrumentation來進行具體的操做。

Instrumentation這個類就是完成對Application和Activity初始化和生命週期的工具類。

前面提到,App和AMS是經過Binder傳遞信息的,ActivityThread接受AMS的命令,而後就是經過Instrumentation真正地建立Activity以及調用Activity的生命週期函數。 好比ApplicationThread接受AMS命令建立Acitivity,最後執行到ActivityThread,經過Instrumentation建立Activity並調用onCreate()生命週期。

//
ActivityThread.performLaunchActivity()
└──  mInstrumentation.newActivity(appContext.getClassLoader(), component.getClassName(), activityClientRecord.intent)
	└── return (Activity)cl.loadClass(className).newInstance()
		
└── mInstrumentation.callActivityOnCreate(activity, r.state)
	└── activity.performCreate()
		└── activity.onCreate(Bundle)
複製代碼

4 Activity的管理方式

前面知道應用啓動以及Activity啓動是一個跨進程通訊的過程,這是由於: 每一個應用都是一個獨立的進程,Activity的生命週期原本就會在不一樣進程之間互相影響,因此須要一個系統進程對全部Activity進行統一管理。 在一個應用程序安裝時,系統會解析出APK中全部Activity的信息,當顯示APK中的用戶界面時,就須要調度Activity的生命週期函數了。 系統進程(SystemServer)中維護了全部Activity的狀態,管理中樞就是ActivityManagerService。

系統進程怎麼管理Activity?

在應用進程這邊,Activity的生命週期都是發生在本進程的主線程裏,由ActivityThread調用Instrumentation完成。 而在系統進程(SystemServer)這頭,則是由AMS維護ActivityRecord等數據結構來進行調度。 Activity管理的數據結構包括: ActivityRecord TaskRecord ActivityStack ActivityDisplay ActivityStackSupervisor ProcessRecord 這些數據結構都屬於JAVA實體類,它們的構建和銷燬都在系統進程中完成。

它們之間的聯繫能夠參考下圖:

出處見水印

圖中的方框能夠理解爲一個包含關係:譬如一個TaskRecord中包含多個ActivityRecord; 圖中的鏈接線能夠理解爲等價關係,譬如同一個ActivityRecord會被TaskRecord和ProcessRecord引用,二者是從不一樣維度來管理ActivityRecord。

  1. ActivityRecord是Activity管理的最小單位,它對應着應用進程的一個Activity;
  2. TaskRecord也是一個棧式管理結構,每個TaskRecord均可能存在一個或多個ActivityRecord,棧頂的ActivityRecord表示當前可見的界面;
  3. ActivityStack是一個棧式管理結構,每個ActivityStack均可能存在一個或多個TaskRecord,棧頂的TaskRecord表示當前可見的任務;
  4. ActivityStackSupervisor管理着多個ActivityStack,但當前只會有一個獲取焦點(Focused)的ActivityStack;
  5. ProcessRecord記錄着屬於一個進程的全部ActivityRecord,運行在不一樣TaskRecord中的ActivityRecord多是屬於同一個 ProcessRecord。

每種數據結構的屬性和行爲

5 Binder運做機制

前面知道應用進程與AMS經過Binder進行了跨進程通訊,那麼Binder到底是如何運做的:

  1. 一個進程空間分爲 用戶空間 & 內核空間(Kernel),即把進程內 用戶 & 內核 隔離開來。
  2. 爲了保證 安全性 & 獨立性,一個進程 不能直接操做或者訪問另外一個進程,即Android的進程是相互獨立、隔離的。
  3. 跨進程間通訊的原理
    • 先經過 進程間 的內核空間進行 數據交互
    • 再經過 進程內 的用戶空間 & 內核空間進行 數據交互,從而實現 進程間的用戶空間 的數據交互
    • Binder,就是充當 鏈接 兩個進程(內核空間)的通道。

下面簡單說說Binder機制 在Android中的具體實現:

5.1 註冊服務

Server進程 建立 一個 Binder 對象,並向 ServiceManager 註冊服務。

5.2 獲取服務

Client進程 須要使用 Server進程的服務時,須 經過Binder驅動 向 ServiceManager進程 獲取相應的Service信息。ServiceManager會返回Server進程的Binder代理對象

5.3 使用服務

  1. client進程的請求數據經過代理binder對象的transact方法,發送到內核空間(Binder驅動?),當前線程被掛起
  2. Binder驅動經過client進程所使用的代理對象找到 對應server進程的真身binder對象,並將數據發送到server進程
  3. server進程收到binder驅動通知,在線程池中進行數據反序列化&調用目標方法(onTransact),並將執行結果發送到Binder驅動(內核空間?)
  4. Binder驅動將server進程的目標方法執行結果,拷貝到client進程的內核空間
  5. Binder驅動通知client進程,以前掛起的線程被喚醒,並收到返回結果

6 總結

  1. 在Android中,全部的應用都是一個獨立的進程。
  2. 每一個應用進程都是由zygote進程fork出來的。
  3. 應用啓動是一個跨進程的複雜工做,應用啓動流程基本是圍繞着SystemServer的ActivityManagerService和應用進程的ActivityThread展開。

參考

  1. Android源碼學習目錄
  2. 應用進程與系統進程的通訊(IActivityManager & IApplicationThread)
  3. 圖文詳解 Android Binder跨進程通訊機制 原理
相關文章
相關標籤/搜索