1. 程式人生 > 實用技巧 >Activity啟動流程分析

Activity啟動流程分析

我們來看一下 startActivity 過程的具體流程。在手機桌面應用中點選某一個 icon 之後,實際上最終就是通過 startActivity 去開啟某一個 Activity 頁面。我們知道 Android 中的一個 App 就相當於一個程式,所以 startActivity 操作中還需要判斷,目標 Activity 的程式是否已經建立,如果沒有,則在顯示 Activity 之前還需要將程式 Process 提前創建出來。假設是從 ActivityA 跳轉到另一個 App 中的 ActivityB,過程如下圖所示:

整個 startActivity 的流程分為 3 大部分,也涉及 3 個程式之間的互動:

  • ActivityA --> ActivityManagerService(簡稱 AMS)
  • ActivityManagerService --> ApplicationThread
  • ApplicationThread --> Activity

ActivityA --> ActivityManagerService 階段

這一過程並不複雜,用一張圖表示具體過程如下:

接下來看下原始碼中做了哪些操作。

Activity 的 startActivity方法

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}

最終呼叫了 startActivityForResult 方法,傳入的 -1 表示不需要獲取 startActivity 的結果。

Activity 的 startActivityForResult

具體程式碼如下所示:

public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
// If this start is requesting a result, we can avoid making
// the activity visible until the result is received. Setting
// this code during onCreate(Bundle savedInstanceState) or onResume() will keep the
// activity hidden during this time, to avoid flickering.
// This can only be done when a result is requested because
// that guarantees we will get information back when the
// activity is finished, no matter what happens to it.
mStartedActivity = true;
} cancelInputsAndStartExitTransition(options);
// TODO Consider clearing/flushing other event sources and events for child windows.
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
// Note we want to go through this method for compatibility with
// existing applications that may have overridden it.
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}

startActivityForResult 也很簡單,呼叫 Instrumentation.execStartActivity 方法。剩下的交給 Instrumentation 類去處理。

解釋說明:

  • Instrumentation 類主要用來監控應用程式與系統互動。
  • 程式碼中的mMainThread 是 ActivityThread 型別,ActivityThread 可以理解為一個程式,在這就是 A 所在的程式。
  • 通過 mMainThread 獲取一個 ApplicationThread 的引用,這個引用就是用來實現程式間通訊的,具體來說就是 AMS 所在系統程式通知應用程式程式進行的一系列操作。

Instrumentation 的 execStartActivity

方法如下:

try {
intent.migrateExtraStreamToClipData();
intent.prepareToLeaveProcess(who);
int result = ActivityTaskManager.getService()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;

通過ActivityTaskManager.getService()獲取獲取ATMS的服務代理,跨程式呼叫ATMS的startActivity方法,下一步就ATMS內部startActivityAsUser()方法處理。

int startActivityAsUser(IApplicationThread caller, String callingPackage,
Intent intent, String resolvedType, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, Bundle bOptions, int userId,
boolean validateIncomingUser) {
enforceNotIsolatedCaller("startActivityAsUser"); userId = getActivityStartController().checkTargetUser(userId, validateIncomingUser,
Binder.getCallingPid(), Binder.getCallingUid(), "startActivityAsUser"); // TODO: Switch to user app stacks here.
return getActivityStartController().obtainStarter(intent, "startActivityAsUser")
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
.setStartFlags(startFlags)
.setProfilerInfo(profilerInfo)
.setActivityOptions(bOptions)
.setMayWait(userId)
.execute(); }

經過多個方法的呼叫,最終通過 obtainStarter 方法獲取了 ActivityStarter 型別的物件,然後呼叫其 execute 方法。在 execute 方法中,會再次呼叫其內部的 startActivityMayWait 方法。

ActivityStarter 的 startActivityMayWait

ActivityStarter 這個類看名字就知道它專門負責一個 Activity 的啟動操作。它的主要作用包括解析 Intent、建立 ActivityRecord、如果有可能還要建立 TaskRecord。startActivityMayWait 方法的部分實現如下:

private int startActivityMayWait(IApplicationThread caller, int callingUid,
String callingPackage, int requestRealCallingPid, int requestRealCallingUid,
Intent intent, String resolvedType, IVoiceInteractionSession voiceSession,
IVoiceInteractor voiceInteractor, IBinder resultTo, String resultWho, int requestCode,
int startFlags, ProfilerInfo profilerInfo, WaitResult outResult,
Configuration globalConfig, SafeActivityOptions options, boolean ignoreTargetSecurity,
int userId, TaskRecord inTask, String reason,
boolean allowPendingRemoteAnimationRegistryLookup,
PendingIntentRecord originatingPendingIntent, boolean allowBackgroundActivityStart) {
// Refuse possible leaked file descriptors
if (intent != null && intent.hasFileDescriptors()) {
throw new IllegalArgumentException("File descriptors passed in Intent");
}
...
ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
0 /* matchFlags */,
computeResolveFilterUid(
callingUid, realCallingUid, mRequest.filterCallingUid));
if (rInfo == null) {
UserInfo userInfo = mSupervisor.getUserInfo(userId);
if (userInfo != null && userInfo.isManagedProfile()) {
// Special case for managed profiles, if attempting to launch non-cryto aware
// app in a locked managed profile from an unlocked parent allow it to resolve
// as user will be sent via confirm credentials to unlock the profile.
UserManager userManager = UserManager.get(mService.mContext);
boolean profileLockedAndParentUnlockingOrUnlocked = false;
long token = Binder.clearCallingIdentity();
try {
UserInfo parent = userManager.getProfileParent(userId);
profileLockedAndParentUnlockingOrUnlocked = (parent != null)
&& userManager.isUserUnlockingOrUnlocked(parent.id)
&& !userManager.isUserUnlockingOrUnlocked(userId);
} finally {
Binder.restoreCallingIdentity(token);
}
if (profileLockedAndParentUnlockingOrUnlocked) {
rInfo = mSupervisor.resolveIntent(intent, resolvedType, userId,
PackageManager.MATCH_DIRECT_BOOT_AWARE
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE,
computeResolveFilterUid(
callingUid, realCallingUid, mRequest.filterCallingUid));
}
}
}
// Collect information about the target of the Intent.
ActivityInfo aInfo = mSupervisor.resolveActivity(intent, rInfo, startFlags, profilerInfo);
...


從上圖可以看出獲取目標 Activity 資訊的操作由 mSupervisor 來實現,它是 ActivityStackSupervisor 型別,從名字也能猜出它主要是負責 Activity 所處棧的管理類。

resolveIntent 中實際上是呼叫系統 PackageManagerService 來獲取最佳 Activity。有時候我們通過隱式 Intent 啟動 Activity 時,系統中可能存在多個 Activity 可以處理 Intent,此時會彈出一個選擇框讓使用者選擇具體需要開啟哪一個 Activity 介面,就是此處的邏輯處理結果。

在 startActivityMayWait 方法中呼叫了一個過載的 startActivity 方法,而最終會呼叫的 ActivityStarter 中的 startActivityUnchecked 方法來獲取啟動 Activity 的結果。

ActivityStarter 的 startActivityUnchecked

解釋說明:

  • 圖中 1 處計算啟動 Activity 的 Flag 值。
  • 註釋 2 處處理 Task 和 Activity 的進棧操作。
  • 註釋 3 處啟動棧中頂部的 Activity。

computeLaunchingTaskFlags 方法具體如下:

這個方法的主要作用是計算啟動 Activity 的 Flag,不同的 Flag 決定了啟動 Activity 最終會被放置到哪一個 Task 集合中。

  • 圖中 1 處 mInTask 是 TaskRecord 型別,此處為 null,代表 Activity 要加入的棧不存在,因此需要判斷是否需要新建 Task。
  • 圖中 2 處的 mSourceRecord 的型別 ActivityRecord 型別,它是用來描述“初始 Activity”,什麼是“初始 Activity”呢?比如 ActivityA 啟動了ActivityB,ActivityA 就是初始 Activity。當我們使用 Context 或者 Application 啟動 Activity 時,此 SourceRecord 為 null。
  • 圖中 3 處表示初始 Activity 如果是在 SingleInstance 棧中的 Activity,這種需要新增 NEW_TASK 的標識。因為 SingleInstance 棧只能允許儲存一個 Activity。
  • 圖中 4 處表示如果 Launch Mode 設定了 singleTask 或 singleInstance,則也要建立一個新棧。

ActivityStackSupervisor 的 startActivityLocked

方法中會呼叫 insertTaskAtTop 方法嘗試將 Task 和 Activity 入棧。如果 Activity 是以 newTask 的模式啟動或者 TASK 堆疊中不存在該 Task id,則 Task 會重新入棧,並且放在棧的頂部。需要注意的是:Task 先入棧,之後才是 Activity 入棧,它們是包含關係。

這裡一下子湧出了好幾個概念 Stack、Task、Activity,其實它們都是在 AMS 內部維護的資料結構,可以用一張圖來描述它們之間的關係。

ActivityStack 的 resumeFocusedStackTopActivityLocked

經過一系列呼叫,最終程式碼又回到了 ActivityStackSupervisor 中的 startSpecificActivityLocked 方法。

ActivityStackSupervisor 的 startSpecificActivityLocked

解釋說明:

  • 圖中 1 處根據程式名稱和 Application 的 uid 來判斷目標程式是否已經建立,如果沒有則代表程式未建立。
  • 圖中 2 處呼叫 AMS 建立 Activity 所在程式。

不管是目標程式已經存在還是新建目標程式,最終都會呼叫圖中紅線標記的realStartActivityLocked 方法來執行啟動 Activity 的操作。

ActivityStackSupervisor 的 realStartActivityLocked

這個方法在 android-27 和 android-28 版本的區別很大,從 android-28 開始 Activity 的啟動交給了事務(Transaction)來完成。

  • 圖中 1 處建立 Activity 啟動事務,並傳入 app.thread 引數,它是 ApplicationThread 型別。在上文 startActivity 階段已經提過 ApplicationThread 是為了實現程式間通訊的,是 ActivityThread 的一個內部類。
  • 圖中 2 處執行 Activity 啟動事務。

Activity 啟動事務的執行是由 ClientLifecycleManager 來完成的,具體程式碼如下:

可以看出實際上是呼叫了啟動事務 ClientTransaction 的 schedule 方法,而這個 transaction 實際上是在建立 ClientTransaction 時傳入的 app.thread 物件,也就是 ApplicationThread。如下所示:

解釋說明:

  • 這裡傳入的 app.thread 會賦值給 ClientTransaction 的成員變數 mClient,ClientTransaction 會呼叫 mClient.scheduleTransaction(this) 來執行事務。
  • 這個 app.thread 是 ActivityThread 的內部類 ApplicationThread,所以事務最終是呼叫 app.thread 的 scheduleTransaction 執行。

到這為止 startActivity 操作就成功地從 AMS 轉移到了另一個程式 B 中的 **ApplicationThread **中,剩下的就是 AMS 通過程式間通訊機制通知 ApplicationThread 執行 ActivityB 的生命週期方法。

ApplicationThread -> Activity

剛才我們已近分析了 AMS 將啟動 Activity 的任務作為一個事務 ClientTransaction 去完成,在 ClientLifecycleManager 中會呼叫 ClientTransaction的schedule() 方法,如下:

public void schedule() throws RemoteException {
mClient.scheduleTransaction(this);
}

而 mClient 是一個 IApplicationThread 介面型別,具體實現是 ActivityThread 的內部類 ApplicationThread。因此後續執行 Activity 生命週期的過程都是由 ApplicationThread 指導完成的,scheduleTransaction 方法如下:

可以看出,這裡還是呼叫了ActivityThread 的 scheduleTransaction 方法。但是這個方法實際上是在 ActivityThread 的父類 ClientTransactionHandler 中實現,具體如下:

呼叫 sendMessage 方法,向 Handler 中傳送了一個 EXECUTE_TRANSACTION 的訊息,並且 Message 中的 obj 就是啟動 Activity 的事務物件。而這個 Handler 的具體實現是 ActivityThread 中的 mH 物件。具體如下:

最終呼叫了事務的 execute 方法,execute 方法如下:

在 executeCallback 方法中,會遍歷事務中的 callback 並執行 execute 方法,這些 callbacks 是何時被新增的呢?

還記得 ClientTransaction 是如何建立被建立的嗎?重新再看一遍:

在建立 ClientTransaction 時,通過 addCallback 方法傳入了 Callback 引數,從圖中可以看出其實是一個 LauncherActivityItem 型別的物件。

LaunchActivityItem 的 execute()

終於到了跟 Activity 生命週期相關的方法了,圖中 client 是 ClientTransationHandler 型別,實際實現類就是 ActivityThread。因此最終方法又回到了 ActivityThread。

ActivityThread 的 handleLaunchActivity

這是一個比較重要的方法,Activity 的生命週期方法就是在這個方法中有序執行,具體如下:

解釋說明:

  • 圖中 1 處初始化 Activity 的 WindowManager,每一個 Activity 都會對應一個“視窗”。
  • 圖中 2 處呼叫 performLaunchActivity 建立並顯示 Activity。
  • 圖中 3 處通過反射建立目標 Activity 物件。
  • 圖中 4 處呼叫 attach 方法建立 Activity 與 Context 之間的聯絡,建立 PhoneWindow 物件,並與 Activity 進行關聯操作。
  • 圖中 5 處通過 Instrumentation 最終呼叫 Activity 的 onCreate 方法。

至此,目標 Activity 已經被成功建立並執行生命週期方法。

總結

詳細查看了 Activity 的啟動在原始碼中的實現流程(不同版本sdk原始碼還是挺大變化的,自己可以去檢視)。這一過程主要涉及 3 個程式間的通訊過程:

1》首先程式 A 通過 Binder 呼叫 AMS 的 startActivity 方法。
2》然後 AMS 通過一系列的計算構造目標 Intent,然後在 ActivityStack 與 ActivityStackSupervisor 中處理 Task 和 Activity 的入棧操作。
3》最後 AMS 通過 Binder 機制,呼叫目標程式中 ApplicationThread 的方法來建立並執行 Activity 生命週期方法,實際上 ApplicationThread 是 ActivityThread 的一個內部類,它的執行最終都呼叫到了 ActivityThread 中的相應方法。

————來自拉勾教育筆記