標籤:

二:Activity啟動

1. 入口(Activity)

android/platform/frameworks/base/master/./core/java/android/app/Activity.java

簡述:啟動一個活動,在Activity$startActivity ---> Activity$startActivityForResult ---> Instrumentation$execStartActivity. 得到一個ActivityResult

我們經常調用startActivity(intent)來開啟一個活動。(註:為保證流程的連貫性,擴展可以略過)

1.1 startActivity

startActivity: 啟動一個新的活動。當活動退出,你不會收到任何信息。該實現重寫了基類(Context$startActivity)版本,提供了啟動活動相關的信息。由於這些增加的信息,不再需要FLAG_ACTIVITY_NEW_TASK啟動標記了。如果未指定,新的活動被添加到調用者的任務棧中。

@Override
public void startActivity(Intent intent, @Nullable Bundle options){
if(options != null){
startActivityForResult(intent, -1, options);
}else{
startActivityForResult(intent, -1);
}
}

1.1.1 擴展 :ActivityOptions

先來看一個方法:

public void overridePendingTransition(int enterAnim, int exitAnim){
try {
ActivityManager.getService().overridePendingTransition(mToken, enterAnim, exitAnim);
}catch (RemoteException e){
}
}

應用開發中,上述方法用來設置Activity的轉場動畫。如果沒有指定,系統默認如下:

ActivityOptions$makeCustomAnimation

public static makeCustomAnimation(Context context,
int enterResId, int exitId, Handler handler, OnAnimationStartedListener listener) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = context.getPackageName();
opts.mAnimationType = ANIM_CUSTOM;
opts.mCustomEnterResId = enterResId;
opts.mCustomExitResId = exitResId;
opts.setOnAnimationStartedListener(listener);
return opts;
}

在材料設計中,還可看到更多漂亮的轉場動畫,比如聯繫人列表進入詳情的頭像伸縮變換轉場動畫。點擊一個條目,頭像會擴大為大圖到詳情的活動頁面,從詳情返回列表,大圖又縮小為小圖。系統實現如下:

private static ActivityOptions makeThumbnailAnimation(View source, Bitmap thumbnail,
int startX, int startY, OnAnimationStartedListener listener, boolean scaleUp) {
ActivityOptions opts = new ActivityOptions();
opts.mPackageName = source.getContext().getPackageName();
opts.mAnimationType = scaleUp ? ANIM_THUMBNAIL_SCALE_UP : ANIM_THUMBNAIL_SCALE_DOWN;
opts.mThumbnail = thumbnail;
int[] pts = new int[2];
source.getLocationOnScreen(pts);
opts.mStartX = pts[0] + startX;
opts.mStartY = pts[1] + startY;
opts.setOnAnimationStartedListener(source.getHandler(), listener);
return opts;
}

注意,這是private方法,配合ActivityOptionsCompat使用。

官方教程

1.2 startActivityForResult

pbulic void startActivityForResult(@RequiresPermission Intent intent, int requestCode, @Nullable Bundle options){
// app冷啟動的首個Activity,mParent為null
if(mParent == null){
// options 為null的時候,使用系統默認,如1.1.1所述
options = transferSpringboardActivityOptions(options);
// * 關鍵方法,這裡返回一個ActivityResult(描述了活動執行的結果,並返回給原始活動)
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this,mMainThread.getApplicationThread(),mToken,this,
intent,requestCode,options);
if(ar != null){
// mMainThread 是ActivityThread的一個實例,在ActivityThread中調用performLaunchActivity時候,反射生成Activity的實例,然後調用Activity$attach方法,把MMainThread實例傳入Activity
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(), ar.getResultData());
}
// 請求碼不小於0的話,在活動退出時候,會被返回給`onActivityResult()`方法
if(requestCode >= 0){
// 如果這次啟動正在請求一個響應結果,系統可以阻止活動可見,直到收到結果。
// 在`onCreate`, `onResume`方法中`startActivityForResult` 設置這個`requestCode`,會保證活動不可見並且避免頁面閃爍。
// 只有在請求到結果時才能完成這些,因為,這樣保證了無論發生什麼都可以在活動結束後獲取到信息。
mStartedActivity = true;
}
// 取消輸入事件,開始轉場動畫
cancelInputsAndStartExitTransition(options);
}else{
if(options != null){
mParent.startActivityFromChild(this, intent, requestCode, options);
}else{
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}

1.2.1 擴展:mMainThread的附著流程

mMainThread是主線程的實例,在一個新的活動實例創建後附著。

系統在ActivityThread$PerformLaunchActivity中,使用Instrumentation$newActivity反射生成Activity實例,然後調用Activity$attach把當前線程實例傳給生成的最新Activity

public Activity newActivity(ClassLoader cl, String className, Intent intent) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
String pkg = intent != null && intent.getComponent() != null ? intent.getComponent().getPackageName() : null;
return getFactory(pkg).instantiateActivity(cl, className, intent);
}

Activity$newActivity ---> AppcomponentFactory$instantiateActivity如下:

public @NonNull Activity instantiateActivity(@NonNull ClassLoader cl, @NonNull String className, @NonNull Intent intent) throws InstaniationException, IllegalAccessException, ClassNotFoundExcption {
return (Activity)cl.loadClass(cl).newInstance();
}

instantiateActivity()允許應用程序重寫活動的創建。可用來生成依賴注入或類載入器修改這些類。

1.2.2 擴展:在DecorView上cancelInput

取消輸入,指的是取消DecorView上的點擊和長按事件。

a) Activity

// Activity$cancelPendingInputsAndStartExitTransition
private void cancelPendingInputsAndStartExitTransition(Bundle options) {
// I: mWidow.peekDecorView() 通過mWindow(PhoneWindow的實例)獲取View的一個實例DecorView
final View decor = mWindow != null ? mWindow.peekDecorView() : null;
if (decor != null){
decor.cancelPendingInputEvents();
}
if (options != null && !isTopOfTask()) {
mActivityTransitionState.startExitOutTransition(this, options);
}
}

cancelPendingInputsAndStartExitTransition()方法中出現了比較重要的角色:mWindow.

attach()方法中有:mWindow = new PhoneWindow(this, window, activityConfigCallback);

attach()又是在哪裡調用的?在ActivityThread$performLaunchActivity().

Activity$onCreate()中,我們經常setContentView(),具體的實現就是在PhoneWindow$setContentView

PhoneWindow會在setContentView的時候檢測DecorView的實例是否存在,如果否,則使用installDecor創建。

b) View(DecorView)

// View$onCancelPendingInputEvents
public void onCancelPendingInputEvents() {
removePerformClickCallback();
cancelLongPress();
mPrivateFlags3 |= PFLAG3_CALLED_SUPER;
}

DecorViewFrameLayout的子類。

DecorView初始化調用過程:

  • Activity$setContentViewActivity$addContentView
  • PhoneWindow$installDecor ---> PhoneWindow$generateDecor --->PhoneWindow$generateLayout() --->DecorView$onResourcesLoaded

2.啟動前監控(Instrumentation)

android/platform/frameworks/base/master/./core/java/android/app/Instrumentation.java

ActivityInstrumentation

2.1 execStartActivity

/**
*
* @param who 開啟當前活動的上下文
* @param contextThread 開啟當前活動的上下文的主線程
* @param token 開啟當前活動的內部令牌,由系統識別,可以為空
* @param target 開啟從而獲取`ActivityResult`的活動,如果當前不是從活動調用的該方法,可以為空
* @param intent 開啟活動的具體意圖
* @param requestCode 此次請求結果的識別碼,小於0說明調用者不期望請求結果
* @param option 附加選項,見1.1.1
*
* @return 返回包含想要的數據的`ActivityResult`,默認返回空
*
*/
public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target, Intent intent, int requestCode, Bundle options) {
IApplicationThread whoThread = (IApplicationThread) contextThread;
// 來源,應用當前展示內容的來源。默認返回`null`,即來源為:當前活動的包名。如果不為空,配合`Intent.EXTRA_REFERRER`使用
Uri referrer = target != null ? target.onProvideReferrer() : null;
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
// ActivityMonitor 配合Instrumentation測試框架使用
if (mActivityMonitors != null) {
synchronized (mSync) {
final int N = mActivityMonitors.size();
for (int i = 0; i < N; i++){
final ActivityMonitor am = mActivityMonitor.get(i);
ActivityResult result = null;
if (am.ignoreMatchingSpecificIntents()) {
result = am.onStartActivity(intent);
}
if (result != null) {
am.mHits++;
return result;
} else if (am.mathch(who, null, intent)) {
am.mHits++;
if (am.isBlocking()) {
return requestCode >= 0 ? am.getResult : null;
}
break;
}
}
}
}
try {
// 當`ACTION_SEND`,`ACTION_SEND_MULTIPLE`,`ACTION_CHOOSER`時,遷移`EXTRA_STREAM`到`ClipData`。ClipData作用是在剪貼板展示剪切的數據
intent.migrateExtraStreamToClipData();
// 準備打開文件選擇器或者剪貼板,這是7.0之後的FileProvide功能。比如啟用打開相冊的意圖時。
intent.papreToLeaveProcess(who);
// 進入ActivityManagerService進行下一步工作
int result = ActivityManager.getService()
.startActivityAsUser(whoThread, who.getBasePackageName(), intent, intent.resolveTypeIfNeeded(who.getContentResolver()), token, resultWho, requestCode, 0, null, options, user.getIndentifier());
// 檢查ActivityResult的返回代碼和ActivityManager定義的那些錯誤常量是否匹配,並拋出響應的異常
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;
}

2.1.1 擴展:android.util.Singleton

execStartActivity(),看到ActivityManager.getService(),獲取ActivityManagerService的單例實例。代碼:

public abstract class Singleton<T> {
private T mInstance;
protected abstract T create();
public final T get() {
synchronized (this){
if (mInstance == null) {
mInstance = create();
}
return mInstance;
}
}
}
private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};

3. 初始化ActivityStarter(ActivityManagerService)

Instrumentation$execStartActivityActivityManagerService$startActivity

3.1startActivityAsUser

/**
*
* @param caller
* @param callingPackage
* @param intent
* @param resolveType
* @param resultTo
* @param resultWho
* @param requestCode
* @param startFlags
* @param profilerInfo
* @param bOptions
* @param userId
* @param validateIncomingUser
*
*/
public final 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("startActivity");
userId = mActivityStartController.checkTargetUser(userId, validateIncomingUser, Binder.getCallingPid(), Binder.getCallingUid, "startActivityAsUser");

return mActivityStartController.obtainStarter(intent, "startActivityAsUser")
// 設置ApplicationThread
.setCaller(caller)
.setCallingPackage(callingPackage)
.setResolvedType(resolvedType)
.setResultTo(resultTo)
.setResultWho(resultWho)
.setRequestCode(requestCode)
// 這裡是0,默認是0
.setStartFlags(startFlags)
// 探查app時的設置,此時為空,默認為空
.setProfilerInfo(profilerInfo)
// 見1.1.1
.setActivityOptions(bOptions)
// 應該等待開啟活動的請求的結果
.setMayWait(userId)
.execute();
}

3.1.1 擴展:應用沙盒

Android平台利用基於用戶的Linux保護機制來識別和隔離應用資源。Android``為每個應用分配獨一無二的用戶ID(UID),並在各自的進程中運行。可將應用分開,保護應用和系統免受惡意應用的攻擊。

Android利用UID設置一個內核級應用沙盒。默認情況下,應用不能彼此交互,而且對操作系統的訪問許可權會受到限制。

由於應用沙盒位於內核層面,因此該安全模型擴展到了原生代碼和系統應用。

3.1.2 擴展:UserHandler

在設備上表示用戶。3.1.1說了,應用默認是隔離的,可以使用shareUid,來共享資源。UserHandle主要涉及android中讓人眼花的各種id管理:

  • UserId:android設備上的多用戶的用戶id
  • UID:跟應用進程相關,一旦安裝到設備就不會改變,每個用戶的每個應用的uid都不一樣
  • APPID:跟包名有關,包名相同appid不同。即使用戶不同,同一個應用appid是一樣的

public final class UserHandle implements Parcelable {
/**
* @hide Range of uids allocated for a user.
* 每個用戶可以有十萬個uid
*/
public static final int PER_USER_RANGE = 100000;

// 根據Userid 生成uid。加入A用戶userId = 0; B為1.appId為10080
public static int getUid(@UserIdInt int userId, @AppIdInt int appId) {
if (MU_ENABLE) {
// A --> 10080
// B --> 100000 + 10080
return userId * PER_USER_RANGE + (appId % PER_USER_RANGE);
} else {
return appId;
}
}

// 傳入uid獲取userid。例如A用戶的UID是10080,B用戶的UID就是100000 + 10080。
// 每個用戶有十萬個uid。即兩個用戶直接相差100000.
public static @UserIdInt int getUserId(int uid) {
if(MU_ENABLE) {
// A / PER_USER_RANGE = 10080 / 100000 = 0
// B / PER_USER_RANGE = (100000 + 10080) / 100000 = 1
return uid / PER_USER_RANGE;
} else {
return UserHandle.USER_SYSTEM;
}
}
// 例如A用戶的UID是10080,B用戶的UID就是100000 + 10080
public static @AppIdInt int getAppId(int uid) {
// A % PER_USER_RANGE = 10080 % 100000 = 10080
// B % PER_USER_RANGE = (100000 + 10080) % 100000 = 10080
// 因此說包名相同,appid必定相同,跟用戶無關。
return uid % PER_USER_RANGE;
}
}

4.啟動模式&任務棧ActivityStarter

ActivityStarter$execu --> startActivityMayWait --> startActivty --> startActivityUnchecked

概述:計算啟動模式的標記;創建或復用任務棧

4.1 startActivityUnchecked

這個方法接近220行,我們看下重點:

/**
* @parmas 這些參數在前面的調用過程中大都有說明
* @return 返回ActivityManager定義的err code,如果START_SUCCESS = 0
*/
private int startActivityUnchecked(...) {
// 初始化一些狀態
setInitialState(...);
// 計算啟動模式
computeLaunchingTaskFlags();
// 計算源stack,如果源正在結束,和它關聯的task也可能為空,就需要用NEW_TASK再建新的task
compupteSourceStack();
// 決定這個新活動是否應該插入已存在的任務棧中。不應該的話,返回空。應該的話返回這個任務棧相關的新活動的ActivityRecord。
ActivityRecord reusedActivity = getReusableIntentActivity();

// ......

// 有可復用的Activity
if (resuedActivity != null) {
// 根據啟動模式,復用或清除活動
}

// 開啟活動的活動不存在了,重點啟動
if (mStartActivity.packageName == null) {
// ......
return START_CLASS_NOT_FOUND;
}

// 如果要啟動的活動和當前棧頂的一樣,根據啟動模式檢測是否需要啟動。
final ActivityRecord top = topStack.topRunningNonDelayedActivityLocked(mNotTop);
final boolean dontStart = top != null && mStartActivity.resultTo == null
&& top.realActivity.equals(mStartActivity.realActivity)
&& top.userId == mStartActivity.userId
&& top.app != null && top.app.thread != null
&& ((mLaunchFlags & FLAG_ACTIVITY_SINGLE_TOP) != 0
|| isLaunchModeOneOf(LAUNCH_SINGLE_TOP, LAUNCH_SINGLE_TASK));
if (dontStart) {
// ......
//不需要重新啟動,傳一個new intent 過去
deliverNewIntent(top);
}

// Should this be considered a new task?
int result = START_SUCCESS;
if (mStartActivity.resultTo == null && mInTask == null && !mAddingToTask
&& (mLaunchFlags & FLAG_ACTIVITY_NEW_TASK) != 0) {
newTask = true;
// 新的任務棧
result = setTaskFromReuseOrCreateNewTask(taskToAffiliate, topStack);
} else if (mSourceRecord != null) {
// 使用源活動的任務棧
result = setTaskFromSourceRecord();
} else if (mInTask != null) {
// 使用目標活動的任務棧
result = setTaskFromInTask();
} else {
// This not being started from an existing activity, and not part of a new task...
// just put it in the top task, though these days this case should never happen.
// 這不是被存在的活動啟動的,也不是新任務棧的一部分,把活動放到棧頂吧,儘管這永遠不應該發生的
setTaskToCurrentTopOrCreateNewTask();
}
if (result != START_SUCCESS) {
return result;
}
// ActivityStack$startActivityLocked 創建預覽窗口
mTargetStack.startActivityLocked(mStartActivity, topFocused, newTask, mKeepCurTransition,
mOptions);
if (mDoResume) {
final ActivityRecord topTaskActivity =
mStartActivity.getTask().topRunningActivityLocked();
if (!mTargetStack.isFocusable()
|| (topTaskActivity != null && topTaskActivity.mTaskOverlay
&& mStartActivity != topTaskActivity)) {
// If the activity is not focusable, we cant resume it, but still would like to
// make sure it becomes visible as it starts (this will also trigger entry
// animation). An example of this are PIP activities.
// Also, we dont want to resume activities in a task that currently has an overlay
// as the starting activity just needs to be in the visible paused state until the
// over is removed.
// 如果活動不可聚焦就不能恢復焦點了。這仍然可以保證活動可見。比如畫中畫的活動。同時,不要恢復焦點的活動上現在有覆蓋層,保持活動可見和暫停狀態,知道覆蓋層移除,比如非全屏的提示框。
mTargetStack.ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
// Go ahead and tell window manager to execute app transition for this activity
// since the app transition will not be triggered through the resume channel.
// 直接通知窗口管理器執行活動間的動畫,因為通過恢復焦點通道去通知執行轉場動畫已經不會被觸發了。
mService.mWindowManager.executeAppTransition();
} else {
// If the target stack was not previously focusable (previous top running activity
// on that stack was not visible) then any prior calls to move the stack to the
// will not update the focused stack. If starting the new activity now allows the
// task stack to be focusable, then ensure that we now update the focused stack
// accordingly.
// 如果目標活動棧不是上一個獲取焦點的(棧中前一個棧頂正在運行的活動不可見),前一個移動活動棧的調用不會更新這個已經獲取焦點的活動棧。
if (mTargetStack.isFocusable() && !mSupervisor.isFocusedStack(mTargetStack)) {
mTargetStack.moveToFront("startActivityUnchecked");
}
mSupervisor.resumeFocusedStackTopActivityLocked(mTargetStack, mStartActivity,
mOptions);
}
} else if (mStartActivity != null) {
mSupervisor.mRecentTasks.add(mStartActivity.getTask());
}
return START_SUCCESS;
}

4.1.1 擴展:ActivityStack、TaskRecord、ActivityRecord、ActivityStackSupervisor關係

  • 一個ActivityRecord對應一個Activity信息,一個Activity可能有多個ActivityRecord,因為Activity可以被啟動多次
  • 一個TaskRecord有多個ActivityRecord,先進後出,稱之為任務棧
  • ActivityStack 管理多個TaskRecord
  • ActivityDisplay主要有Home Stack,App Stack兩個棧
  • 一般情況下,沒有分屏以及虛擬屏的情況下,ActivityDisplay,ActivityStackSupervisor都是系統唯一

4.1.2 ActivityStack$startActivityLocked

啟動模式和任務棧確定了。要先顯示一個starting window作為預覽窗口。

這裡涉及到WindowManager,WindowManagerService。

5.正式啟動(ActivityStackSupervisor & ActivityStack)

ActivityStackSupervisor$resumeFocusedStackTopActivityLocked --> ActivityStack$resumeTopActivityUncheckedLocked --> ActivityStack$resumeTopActivityInnerLocked

---> ActivityStackSupervisor$startSpecificActivityLocked --> ActivityStackSupervisor$realStartActivityLocked

5.1 ActivityStack$resumeTopActivityInnerLocked

private boolean resumeTopActivityInnerLocked(ActivityRecord prev, ActivityOptions options) {
// ... 系統沒在啟動中並且系統也不是已啟動,返回false

// 找到前台棧中,棧頂第一個非finishing狀態的Activity,如果沒有找到返回false
final ActivityRecord next = topRunningActivityLocked(true /* focusableOnly */)
// top running之後的任意處於初始化狀態並且有顯示starting window,移除starting window
mStackSupervisor.cancelInitializingActivities();
if (!hasRunningActivity) {
// There are no activities left in the stack, lets look somewhere else.
// 【* 5.1.1】當前棧沒有活動了,找到下一個合適的,可以獲取焦點的棧啟動,如果還沒找到,就啟動桌面 5.1.1
return resumeTopActivityInNextFocusableStack(prev, options, "noMoreActivities");
// ...
// 下一步判斷:如果棧頂是要恢復焦點的活動,啥都不做。
// 如果正在睡眠,沒有待獲取焦點的活動,並且棧頂活動是暫停狀態,這是我們想要的狀態。
// 確保擁有此活動的用戶是啟動了的。如果沒有,就保持那樣吧,別人應該會帶來另一個用戶的活動到棧頂的。
// 【* 5.1.2】暫停上一個活動,這是阻塞的,在暫停完成之前,什麼也不會做的。
boolean pausing = mStackSupervisor.pauseBackStacks(userLeaving, next, false);
if (mResumedActivity != null) {
if (DEBUG_STATES) Slog.d(TAG_STATES,
"resumeTopActivityLocked: Pausing " + mResumedActivity);
pausing |= startPausingLocked(userLeaving, false, next, false);
}
// ...
if (next.app != null && next.app.thread != null) {

}else {
// 新的Activity
mStackSupervisor.startSpecificActivityLocked(next, true, true);
}
}
}
void startSpecificActivityLocked(ActivityRecord r,boolean andResume, boolean checkConfig) {
if (app != null && app.thread != null) {
// 【* 5.2】啟動Activity
realStartActivityLocked(r, app, andResume, checkConfig);
} else {
// 【* 5.1.3】 創建新進程
mService.startProcessLocked(...);
}
}

5.1.1 啟動桌面

  • ActivityManagerService$startHomeActivityLocked,根據HomeIntent生成一個新的ActivityInfo
  • 下一步來到ActivityStarterController$startHomeActivity,在這裡,調用ActivityStackSupervisor$moveHomeStackTaskToTop把桌面任務棧移至頂部,然後生成一個ActivityStarter來執行
  • 這一次由於沒有設置userid,所以不會像上面那樣走startActivityMayWait,而是走startActivity

boolean startHomeActivityLocked(int userId, String reason) {
if (mFactoryTest == FactoryTest.FACTORY_TEST_LOW_LEVEL
&& mTopAction == null) {
// We are running in factory test mode, but unable to find
// the factory test app, so just sit around displaying the
// error message and dont try to start anything.
return false;
}
Intent intent = getHomeIntent();
ActivityInfo aInfo = resolveActivityInfo(intent, STOCK_PM_FLAGS, userId);
if (aInfo != null) {
intent.setComponent(new ComponentName(aInfo.applicationInfo.packageName, aInfo.name));
// Dont do this if the home app is currently being
// instrumented.
aInfo = new ActivityInfo(aInfo);
aInfo.applicationInfo = getAppInfoForUser(aInfo.applicationInfo, userId);
ProcessRecord app = getProcessRecordLocked(aInfo.processName,
aInfo.applicationInfo.uid, true);
if (app == null || app.instr == null) {
intent.setFlags(intent.getFlags() | FLAG_ACTIVITY_NEW_TASK);
final int resolvedUserId = UserHandle.getUserId(aInfo.applicationInfo.uid);
// For ANR debugging to verify if the user activity is the one that actually
// launched.
final String myReason = reason + ":" + userId + ":" + resolvedUserId;
mActivityStartController.startHomeActivity(intent, aInfo, myReason);
}
} else {
Slog.wtf(TAG, "No home screen found for " + intent, new Throwable());
}

return true;
}

5.1.2 暫停上一個Activity

  • 獲取到ClientLifecycleManager的實例,並調用scheduleTransaction
  • 下一步調用ClientTransaction$schedule方法,transaction.getClient()IApplicationThread類型的,ActivityThread$ApplicationThread實現了該介面。這裡隱隱然,我們就能猜到又要用到ActivityThread$H來處理了
  • 下一步我們看到ApplicationThread果然實現了該方法,但是具體的執行調用是ActivityThread.this.scheduleTransaction(transaction);
  • 下一步,其實真正實現該方法的是ActivityThread的父類:ClientTransactionHandler,在這裡發送消息到ActivityThread$H中去:sendMessage(ActivityThread.H.EXECUTE_TRANSACTION, transaction);
  • 下一步,調用TransactionExecutor$executeLifecycleState,根據LifecycleItem的內容來轉換狀態,即我們一開始生成的PauseActivityItem
  • 下一步,執行PauseActivityItem$execute, PauseActivityItem$postExecute,根據execute的參數client,發現最終執行的是ActivityThread$performPauseActivity,其中,如果需要保存狀態,調用callActivityOnSaveInstanceState
  • 下一步,調用Instrumentation$callActivityOnPause,然後調用Activity$performPause
  • 最終,看到了熟悉的onPause()

// 開始暫停當前獲取焦點的活動。如果活動已經暫停或者沒有獲取焦點,這就是錯誤的調用了。
final boolean startPausingLocked(boolean userLeaving, boolean uiSleeping,
ActivityRecord resuming, boolean pauseImmediately) {
// ...

if (prev.app != null && prev.app.thread != null) {
if (DEBUG_PAUSE) Slog.v(TAG_PAUSE, "Enqueueing pending pause: " + prev);
try {
EventLogTags.writeAmPauseActivity(prev.userId, System.identityHashCode(prev),
prev.shortComponentName, "userLeaving=" + userLeaving);
mService.updateUsageStats(prev, false);

mService.getLifecycleManager().scheduleTransaction(prev.app.thread, prev.appToken, PauseActivityItem.obtain(prev.finishing, userLeaving,
prev.configChangeFlags, pauseImmediately));
}
}
// ...
}

5.1.3 新建進程

  • ActivityStackSupervisor$startSpecificActivityLocked進入ActivityManagerService$startProcessLocked,與ActivityRecord類似地,生成一個ProcessRecord對象

  • startProcessLocked進入Process$start,這裡注意一個參數:String entryPoint = "android.app.ActivityThread";

  • 上一步的entryPoint組裝成argsForZygote,同時由openZygoteSocketIfNeeded生成一個ZygoteState用於進程間進行Socket通信,這兩個對象交由ZygoteProcess$zygoteSendArgsAndGetResult

  • 下一步我們去ZygoteInit看看,上面我們openZygoteSocketIfNeeded時,創建的socket的名字是:zygote。在ZygoteInit$main中,我們同樣發現:String socketName = "zygote";zygoteServer.registerServerSocketFromEnv(socketName);,同時在這裡為zygote命令連接註冊了socket服務

  • Zygote$main中,我們看到,有forkSsytemServer,顧名思義,這是啟動系統服務進程的,暫時忽略,繼續看下去:caller = zygoteServer.runSelectLoop(abiList);

  • 下一步,看到runSelectLoop中維護了ZygoteConnection的一個ArrayList,用來存放ActivityManagerService發來的請求數據,接著調用:

java ZygoteConnection connection = peers.get(i); final Runnable command = connection.processOneCommand(this);

  • ZygoteConnection$processOneCommand中,先去獲取pid: pid = Zygote.forkAndSpecialize(...),如果pid == 0 表示是子進程,處理函數:handleChildProc,否則表示交由父進程處理: handleParentProc

  • handleChildProc中來到ZygoteInit$childZygoteInit,跟入一看,來到RuntimeInit$findStaticMain,在這裡,根據類名,及我們上面的entryPoint變數的值,這裡是android.app.ActivityThread,反射運行其main方法

5.2 Activity啟動

a) 在 5.1.3中,我們走到了ActivityThread$main方法中,這裡的主要工作:

public static void main(String[] args) {
// ...
// 為主線程準備looper
Looper.prepareMainLooper();
// 把主線程添加到ActivityManagerService
ActivityThread thread = new ActivityThread();
thread.attach(false, startSeq);
// 開啟無線循環
Looper.loop();
// ...
}

b) 在ActivityThread$attach中:

private void attach(boolean system, long startSeq) {
// ...
if (!system) {
// 非系統應用
final IActivityManager mgr = ActivityManager.getService();
try {
mgr.attachApplication(mAppThread, startSeq);
} catch (RemoteException ex) {
throw ex.rethrowFromSystemServer();
}
BinderInternal.addGcWatcher(new Runnable() {
//此處為GC策略:當已使用內存大於總內存的3/4時,通知AMS釋放資源
if (dalvikUsed > ((3*dalvikMax)/4)) {
mgr.releaseSomeActivities(mAppThread);
}
}

} else {
// 系統應用,直接調用其Application的onCreate方法
ContextImpl context = ContextImpl.createAppContext(this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
}
// 最後為根空間添加配置改變的回調
ViewRootImpl.ConfigChangedCallback configChangedCallback = ...

// ...
}

c) 下面,來到了ActivityManagerService$attachApplication,傳入當前的主線程實例,然後來到attachApplicationLocked方法,在這裡,看到了中國人民的老朋友ActivityStackSupervisor的實例:

private final boolean attachApplicationLocked(IApplicationThread thread,
int pid, int callingUid, long startSeq) {
// ...
// 先根據pid判斷一番,ProcessRecord是否存在,如果進程不存在,通知系統回收pid,返回false
ProcessRecord app;
if (app == null) {
if (pid > 0 && pid != MY_PID) {
killProcessQuiet(pid);
//TODO: killProcessGroup(app.info.uid, pid);
} else {
thread.scheduleExit();
}
}
// 如果應用程序的記錄和前一個進程關聯著,清除一下
if (app.thread != null) {
handleAppDiedLocked(app, true, true);
}

// 這裡涉及到ContentProvider的生成,略
// 這裡涉及profiler的信息生成,用於Android Profiler,略
// 調用ApplicationThread的handleBindApplication方法
thread.bindApplication(...);
// 更新LRU中進程的次序
updateLruProcessLocked(app, false, null);
// 這裡進入ActivityStackSupervisor
mStackSupervisor.attachApplicationLocked(app)
// ...
}

d) 我們看下ActivityStackSupervisor$realStartActivityLocked

final boolean realStartActivityLocked(ActivityRecord r, ProcessRecord app,
boolean andResume, boolean checkConfig) throws RemoteException {
// ...
// Create activity launch transaction.
// 這裡我們發現上面那句話,「創建活動登錄合約」,然後看到了LaunchActivityItem,根據我們追蹤PauseActivityItem的經驗,二者類似
// ...
}

e) 然後ActivityThread$handleLaunchActivity --> ActivityThread$performLaunchActivity

private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
// ...
// 反射生成Activity實例
Activity activity = null;
java.lang.ClassLoader cl = appContext.getClassLoader();
activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
// 設置base 上下文,關聯進程,主線程
activity.attach(...);
// 調用Instrumentation的創建方法
mInstrumentation.callActivityOnCreate(...)
// ...
}

f) 然後Activity$performCreate,這裡我們看到了熟悉的onCreate.我們知道TransactionExecutor 執行所有的合約executeCallbacks(transaction),executeLifecycleState(transaction),在生命周期的轉換里,具體實現如下TransactionExecutorHelper$getLifeCyclePath

public IntArray getLifecyclePath(int start, int finish, boolean excludeLastState) {
// ...
// 清除生命周期IntArray,這是Android自定義的數據結構
mLifecycleSequence.clear();
// 下面就是Activity各個生命周期對應的關係了
// 如果終止狀態大於等於開始狀態,那麼從開始狀態的下一個狀態累加放入生命周期序列里
if (finish >= start) {
// just go there
for (int i = start + 1; i <= finish; i++) {
mLifecycleSequence.add(i);
}
} else { // finish < start, cant just cycle down
// 以暫停開始,以恢復焦點結束,那麼下一個狀態是恢復焦點
if (start == ON_PAUSE && finish == ON_RESUME) {
// Special case when we can just directly go to resumed state.
mLifecycleSequence.add(ON_RESUME);
} else if (start <= ON_STOP && finish >= ON_START) {
// Restart and go to required state.
// 開始狀態小於stop,結束狀態大於start,那麼resume,pause狀態加入
// Go to stopped state first.
for (int i = start + 1; i <= ON_STOP; i++) {
mLifecycleSequence.add(i);
}
// restart狀態加入
mLifecycleSequence.add(ON_RESTART);
// Go to required state 加入全狀態
for (int i = ON_START; i <= finish; i++) {
mLifecycleSequence.add(i);
}
} else {
// Relaunch and go to required state
// 加入全狀態,即重啟
// Go to destroyed state first.
for (int i = start + 1; i <= ON_DESTROY; i++) {
mLifecycleSequence.add(i);
}
// Go to required state
for (int i = ON_CREATE; i <= finish; i++) {
mLifecycleSequence.add(i);
}
}
}
// ...
}
public static final int UNDEFINED = -1;
public static final int PRE_ON_CREATE = 0;
public static final int ON_CREATE = 1;
public static final int ON_START = 2;
public static final int ON_RESUME = 3;
public static final int ON_PAUSE = 4;
public static final int ON_STOP = 5;
public static final int ON_DESTROY = 6;
public static final int ON_RESTART = 7;

g) 根據上一步,可以知道create之後是start, resume兩個狀態。這裡流程和pause, launch類似。至此,Activity啟動完畢。

6 Activity的結束

我們曾經分析過pause狀態,也知道接下來是stop, desotry狀態,那麼這兩個狀態是在哪裡調用的?

ActivityThread$handleResumeActivity中,我們處理了新活動的獲取焦點,在這個方法最後一句:

Looper.myQueue().addIdleHandler(new Idler());

發現這個Idler調用了ActivityManagerService$activityIdle,然後調用了ActivityStackSupervisor$activityIdleInternalLocked:

if (r.finishing) {
stack.finishCurrentActivityLocked(r, ActivityStack.FINISH_IMMEDIATELY, false,
"activityIdleInternalLocked");
} else {
stack.stopActivityLocked(r);
}

可以看到,stop,finish都有了。

接下來我們預想的StopActivityItem, FinishActivityItem合約也出現了,這樣又一次調用到ActivityThread的各種相關方法。實際追蹤發現其實是DestoryActivityItem。

這裡多提一句,我們在一開始調用startActivity --> startActivityForResult,期望能夠得到一個ActivityResult。這個結果,後來在ActivityThread$sendActivityResult中,通過ActivityResultItem管理轉發,這也是個合約,和DestoryActivityItem都是ClientTransactionItem的子類。鏈路如下:

ActivityThread$sendActivityResult --> ActivityResultItem$execute --> ActivityThread$handleSendResult --> ActivityThread$deliverResults --> Activity$dispatchActivityResult --> Activity$onActivityResult

推薦閱讀:

十幾塊能買到哪些好用的包月服務?
2017 我所分享的技術文章總結(下)
請問(2018.8),買iphone6s和同價位的安卓哪個更好一些?
從CarPlay到YunOS,主流車載系統有哪些?
Google Flutter From Scratch:使用小部件構建應用程序

TAG:Android |