startActivity啟動流程學習
舉例,當程序A呼叫startActivity方法,啟動程序B,並開啟B的Activity,這個過程是怎樣的?以下是學習筆記,基於Android 9.0,線上原始碼檢視:ofollow,noindex">https://www.androidos.net.cn/android/9.0.0_r8/xref
-
程序A呼叫startActivity方法,本質上是通過binder通訊,呼叫IActivityManager#startActivity方法
-
如果該呼叫在Activity中,方法呼叫過程:
ContextImpl#startActivity -> mInstrumentation#execStartActivity -> IActivityManager#startActivity
備註:IActivityManager物件儲存在程序A的單例IActivityManagerSingleton中,程序啟動時查詢ServiceManager#getService獲得,具有快取作用
-
-
SystemServer程序的binder執行緒響應此請求,ActivityManagerService#startActivity方法被呼叫,過程如下(備註:過程較複雜,涉及Activity,ActivityStack等一連串操作):
- ActivityManagerService#startActivity
-
ActivityStarter#startActivityMayWait,其中會呼叫PMS#resolveIntent方法,解析Intent獲得ResolveInfo,並建立ActivityInfo等物件
ResolveInfo rInfo = mSupervisor.resolveIntent((intent, resolvedType, userId, xxx);
-
ActivityStarter#startActivity,其中會建立即將開啟的ActivityRecord等
ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, xxxx);
- ActivityStack#resumeTopActivityUncheckedLocked
- ActivityStackSupervisor#startSpecificActivityLocked,這裡很重要,會根據目標程序名,查詢程序是否已存在,如果存在,將執行realStartActivityLocked方法;如果不存在,將執行startProcessLocked,冷啟動目標程序
void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) { // Is this activity's application already running? ProcessRecord app = mService.getProcessRecordLocked(r.processName, r.info.applicationInfo.uid, true); ...... if (app != null && app.thread != null) { ....... realStartActivityLocked(r, app, andResume, checkConfig); return; } ...... mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
-
假設這裡要啟動新程序,執行
ActivityManagerService#startProcessLocked -> ActivityManagerService#startProcess -> Process#start -> ZygoteProcess#startViaZygote
a. 利用Socket通訊通知Zygote程序,孵化出程序B(判斷是否啟動成功:Socket返回結果中目標程序pid < 0,則啟動失敗,丟擲ZygoteStartFailedEx異常)
b. 程序B啟動成功後,方法繼續執行到ActivityManagerService#handleProcessStartedLocked,其中pid等資訊填入事先建立好的ProcessRecord物件中,其對應每一個App程序,儲存在陣列mPidsSelfLocked中
mPidsSelfLocked是AMS的成員變數,型別:SparseArray<E>,本質是利用2個數組儲存,key和object各一個。此處pid為key,ProcessRecord物件為object。查詢方法為二分法
-
目標程序B啟動後,入口函式是ActivityThread#main(靜態方法),過程如下:
-
初始化環境,建立ActivityThread物件,其子物件中如:
a. mH,型別:ActivityThread#H,作用:主執行緒的Handler,處理如BIND_APPLICATION,CREATE_SERVICE等幾十個訊息型別,訊息來源是mAppThread
b. mAppThread,型別:ActivityThread#ApplicationThread,作用:binder實體物件,用於接收AMS的binder呼叫,並將訊息轉發mH處理
c. mInstrumentation,型別:Instrumentation,很重要的基礎類,官方描述:用來監控系統與應用的所有互動,例如newApplication,callApplicationOnCreate,callActivityOnCreate等都是其子方法
注:平時解bug時發現,一些App的外掛化hook框架,會動態替換mInstrumentation物件,如達到啟動Activity時替換目標頁面等目的
-
呼叫ActivityThread#attach方法 [重要],其中呼叫IActivityManager#attachApplication,與AMS進行binder通訊,將程序的mAppThread傳給AMS,類似進行註冊
mAppThread,型別:ApplicationThread extends IApplicationThread.Stub,本質為binder物件,作為服務端,將來接收AMS的binder呼叫
- main方法最後呼叫Looper.loop,程序B的主執行緒進入迴圈處理訊息模式
-
初始化環境,建立ActivityThread物件,其子物件中如:
public static void main(String[] args) { // 3.1(Binder物件,繼承自IApplicationThread.Stub) Environment.initForCurrentUser(); Looper.prepareMainLooper(); // 3.2 ActivityThread thread = new ActivityThread(); thread.attach(false, startSeq); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } // 3.3 Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
-
SystemServer程序,作為服務端,接收到程序B的attachApplication方法呼叫,過程如下:
- 從前面提到的mPidsSelfLocked中提取app物件,型別:ProcessRecord,存入applicationThread等資訊
- 註冊死亡回撥,以便App程序掛掉後,AMS能及時得到通知,並清除App的相關資訊
- 發起binder通訊,利用applicationThread物件,呼叫程序B的ApplicationThread#bindApplication方法,經訊息轉發,最終由程序B主執行緒的mH物件處理,訊息型別:BIND_APPLICATION
-
檢查是否有頁面要顯示
ActivityStackSupervisor#attachApplicationLocked ->ActivityStackSupervisor#realStartActivityLocked(如果程序B有位於棧頂的Activity需顯示) -> ClientTransaction#schedule
利用applicationThread物件,呼叫程序B的ApplicationThread#scheduleTransaction方法,經訊息轉發,最終由程序B主執行緒的mH物件處理,操控Activity生命週期等,訊息型別:EXECUTE_TRANSACTION -
檢查是否有服務要啟動
ActiveServices#attachApplicationLocked -> ActivityStackSupervisor#realStartServiceLocked
利用applicationThread物件,呼叫程序B的ApplicationThread#scheduleCreateService方法,後續同上類似 - 檢查是否有廣播訊息要處理,不再詳細描述
- 檢查是否有BackupAgent要建立,不再詳細描述
private final boolean attachApplicationLocked(IApplicationThread thread, int pid, int callingUid, long startSeq) { // 4.1 ProcessRecord app; if (pid != MY_PID && pid >= 0) { app = mPidsSelfLocked.get(pid); // 4.2 try { AppDeathRecipient adr = new AppDeathRecipient( app, pid, thread); thread.asBinder().linkToDeath(adr, 0); app.deathRecipient = adr; } catch (RemoteException e) { // ... } // 4.3 thread.bindApplication(processName, appInfo, providers, xxx); // 4.4 mStackSupervisor.attachApplicationLocked(app)) { didSomething = true; } // 4.5 didSomething |= mServices.attachApplicationLocked(app, processName); // 4.6 didSomething |= sendPendingBroadcastsLocked(app); // 4.7 thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, xxx); return true; }
總結:
至此經前4步,至少5次關鍵的跨程序通訊,startActivity過程已基本完成。簡單描述:
- 程序A呼叫IActivityManager#startActivity方法,發起binder通訊
- SystemServer程序作為服務端,AMS會檢查程序B是否存在,如無,發起Socket通訊,通知Zgyote程序孵化出程序B
- 程序B啟動後,呼叫IActivityManager#attachApplication方法,發起binder通訊
- SystemServer程序作為服務端,AMS會儲存程序B的相關資訊,發起binder通訊,呼叫程序B的bindApplication,scheduleTransaction等方法
- 程序B依次完成Application#onCreate等初始化,並執行scheduleTransaction,初始化要顯示的Activity,過程結束