前言
關(guān)于 Activity 啟動(dòng),Android 中場(chǎng)景大致有兩個(gè):
- 從 launcher 中啟動(dòng)應(yīng)用,觸發(fā)該應(yīng)用默認(rèn) Activity 的啟動(dòng)。這種 Activity 都是在新進(jìn)程和新的任務(wù)棧中啟動(dòng)的,所以涉及到新進(jìn)程和新任務(wù)棧的初始化
- 應(yīng)用程序內(nèi)部啟動(dòng)非默認(rèn) Activity, 被啟動(dòng)的 Activity 一般在原來(lái)的進(jìn)程和任務(wù)棧中啟動(dòng)
本文主要介紹第一種場(chǎng)景
背景知識(shí)
進(jìn)程與線程
由于 Activity 的啟動(dòng)流程中涉及了大量的進(jìn)程間通信芯砸,例如:ActivityManagerService 和 ActivityStack 位于同一進(jìn)程,ApplicationThread 和 ActivityThread 位于同一進(jìn)程给梅。所以有必要梳理一下進(jìn)程與線程的區(qū)別
從操作系統(tǒng)的角度看,進(jìn)程和線程有什么區(qū)別双揪?
- 進(jìn)程有獨(dú)立的地址空間动羽,一個(gè)進(jìn)程崩潰后,在保護(hù)模式下不會(huì)對(duì)其他進(jìn)程造成影響渔期。
- 線程沒(méi)有獨(dú)立的地址空間运吓,線程只是進(jìn)程所屬進(jìn)程的不同執(zhí)行路徑
從 JVM 的運(yùn)行時(shí)數(shù)據(jù)分區(qū)的角度,進(jìn)程和線程有什么關(guān)系疯趟?
JVM 運(yùn)行時(shí)數(shù)據(jù)分區(qū)如圖所示:
由圖可以看出如下幾點(diǎn):
- 一個(gè)進(jìn)程中的多個(gè)線程共享堆區(qū)和方法區(qū)
- 每個(gè)線程擁有自己的程序計(jì)數(shù)器拘哨,虛擬機(jī)棧,本地方法棧
Activity 啟動(dòng)模式
啟動(dòng)模式橫向?qū)Ρ?/h4>
四中啟動(dòng)模式的概念就不詳述了信峻,這里只是對(duì)關(guān)鍵點(diǎn)做出橫向?qū)Ρ染肭啵鐖D所示
一些鮮為人知的 Intent Flag
下面是一些 Intent Flag 及介紹,如圖所示
Activity 啟動(dòng)流程源碼分析
概念
首先介紹一下 Activity 啟動(dòng)流程涉及到的核心類
ActivityThread
ActivityThread 的作用是管理應(yīng)用程序主線程的相關(guān)流程盹舞,例如管理與處理 activity manager 發(fā)送的請(qǐng)求产镐,這些請(qǐng)求可以來(lái)自 Activity 或 BroadCast
Instrumentation
它用來(lái)監(jiān)控應(yīng)用程序與系統(tǒng)之間的交互
ActivityManagerService (AMS)
AMS(ActivityManagerService)是貫穿Android系統(tǒng)組件的核心服務(wù),負(fù)責(zé)了系統(tǒng)中四大組件的啟動(dòng)踢步、切換癣亚、調(diào)度以及應(yīng)用進(jìn)程管理和調(diào)度工作
執(zhí)行流程
Step 1. Launcher.startActivitySafely
/**
* Launches the intent referred by the clicked shortcut.
*
* @param v The view representing the clicked shortcut.
*/
public void onClick(View v) {
// Make sure that rogue clicks don't get through while allapps is launching, or after the
// view has detached (it's possible for this to happen if the view is removed mid touch).
if (v.getWindowToken() == null) {
return;
}
if (!mWorkspace.isFinishedSwitchingState()) { // Launcher 程序在監(jiān)聽(tīng)點(diǎn)擊事件時(shí)會(huì)判斷頁(yè)面是否正在滑動(dòng),如果在滑動(dòng)則不響應(yīng)點(diǎn)擊應(yīng)用程序 icon 的事件
return;
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) { // 處理點(diǎn)擊應(yīng)用程序 icon 的場(chǎng)景
// Open shortcut
final Intent intent = ((ShortcutInfo) tag).intent;
int[] pos = new int[2];
v.getLocationOnScreen(pos);
intent.setSourceBounds(new Rect(pos[0], pos[1],
pos[0] + v.getWidth(), pos[1] + v.getHeight()));
boolean success = startActivitySafely(v, intent, tag); // startActivitySafely 這里主要會(huì)判斷待啟動(dòng)的 Activity 是否存在获印,若不存在則會(huì)報(bào) ActivityNotFound Exception 并捕獲
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) { // 處理點(diǎn)擊文件夾的場(chǎng)景
···
} else if (v == mAllAppsButton) {
···
}
}
Launcher 程序在監(jiān)聽(tīng)點(diǎn)擊事件時(shí)會(huì)判斷頁(yè)面是否正在滑動(dòng)述雾,如果在滑動(dòng)則不響應(yīng)點(diǎn)擊應(yīng)用程序 icon 的事件
Step 2. Activity.startActivity()
由于 Launcher 繼承于 Activity, 因此代碼執(zhí)行流程到了 startActivity()
。
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
@Override
public void startActivity(Intent intent) {
startActivityForResult(intent, -1);
}
......
startActivity 最終是調(diào)用 startActivityForResult
, 傳入的參數(shù)為 -1 代表這次啟動(dòng) Activity 不需要獲取結(jié)果
Step 3. Activity.startActivityForResult()
public class Activity extends ContextThemeWrapper
implements LayoutInflater.Factory,
Window.Callback, KeyEvent.Callback,
OnCreateContextMenuListener, ComponentCallbacks {
......
public void startActivityForResult(Intent intent, int requestCode) {
if (mParent == null) {
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode);
......
} else {
......
}
......
這里有兩個(gè)有趣的場(chǎng)景:
-
startActivityForResult()
不能用于啟動(dòng)singleTask
為啟動(dòng)模式的 Activity, 否則會(huì)從onActivityForResult()
中收到 cancel 事件 - 假設(shè)有這樣一個(gè)場(chǎng)景,Activity A 啟動(dòng) Activity B, 如果在 A 的 onCreate() / onResume() 中調(diào)用
startActivityForResult()
, 并且傳入的 requestCode >= 1, 那么 A Activity 所掛鉤的 Window 將暫時(shí)不可見(jiàn)玻孟,這種不可見(jiàn)狀態(tài)直到收到 B Activity 的回調(diào)結(jié)果唆缴。目的是防止 Activity 跳轉(zhuǎn)時(shí) UI 閃爍
Step 4. ActivityStack.startActivityUncheckedLocked
final int startActivityUncheckedLocked(ActivityRecord r,
ActivityRecord sourceRecord, Uri[] grantedUriPermissions,
int grantedMode, boolean onlyIfNeeded, boolean doResume) {
final Intent intent = r.intent;
final int callingUid = r.launchedFromUid;
int launchFlags = intent.getFlags();
// We'll invoke onUserLeaving before onPause only if the launching
// activity did not explicitly state that this is an automated launch.
mUserLeaving = (launchFlags&Intent.FLAG_ACTIVITY_NO_USER_ACTION) == 0;
......
ActivityRecord notTop = (launchFlags&Intent.FLAG_ACTIVITY_PREVIOUS_IS_TOP)
!= 0 ? r : null;
// If the onlyIfNeeded flag is set, then we can do this if the activity
// being launched is the same as the one making the call... or, as
// a special case, if we do not know the caller then we count the
// current top activity as the caller.
if (onlyIfNeeded) {
......
}
if (sourceRecord == null) {
......
} else if (sourceRecord.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
......
} else if (r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK) {
......
}
if (r.resultTo != null && (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
......
}
boolean addingToTask = false;
if (((launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0 &&
(launchFlags&Intent.FLAG_ACTIVITY_MULTIPLE_TASK) == 0)
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_TASK
|| r.launchMode == ActivityInfo.LAUNCH_SINGLE_INSTANCE) {
// If bring to front is requested, and no result is requested, and
// we can find a task that was started with this same
// component, then instead of launching bring that one to the front.
if (r.resultTo == null) {
// See if there is a task to bring to the front. If this is
// a SINGLE_INSTANCE activity, there can be one and only one
// instance of it in the history, and it is always in its own
// unique task, so we do a special search.
ActivityRecord taskTop = r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE
? findTaskLocked(intent, r.info)
: findActivityLocked(intent, r.info);
if (taskTop != null) {
......
}
}
}
......
if (r.packageName != null) {
// If the activity being launched is the same as the one currently
// at the top, then we need to check if it should only be launched
// once.
ActivityRecord top = topRunningNonDelayedActivityLocked(notTop);
if (top != null && r.resultTo == null) {
if (top.realActivity.equals(r.realActivity)) {
......
}
}
} else {
......
}
boolean newTask = false;
// Should this be considered a new task?
if (r.resultTo == null && !addingToTask
&& (launchFlags&Intent.FLAG_ACTIVITY_NEW_TASK) != 0) {
// todo: should do better management of integers.
mService.mCurTask++;
if (mService.mCurTask <= 0) {
mService.mCurTask = 1;
}
r.task = new TaskRecord(mService.mCurTask, r.info, intent,
(r.info.flags&ActivityInfo.FLAG_CLEAR_TASK_ON_LAUNCH) != 0);
......
newTask = true;
if (mMainStack) {
mService.addRecentTaskLocked(r.task);
}
} else if (sourceRecord != null) {
......
} else {
......
}
......
startActivityLocked(r, newTask, doResume);
return START_SUCCESS;
}
由于我們是總 launcher 啟動(dòng) Activity, 因此 當(dāng)前的前臺(tái)任務(wù)棧棧頂是 launcher Activity, 因此需要?jiǎng)?chuàng)建一個(gè)新的 ActivityStack, 和 ActivityRecord 對(duì)象,將 ActivityRecord 放入其中取募,最終這個(gè)新創(chuàng)建的 ActivityStack 被保存到 AMS 中
Step 5. Activity.resumeTopActivityLocked
// If the top activity is the resumed one, nothing to do.
if (mResumedActivity == next && next.state == ActivityState.RESUMED) {
......
}
// If we are sleeping, and there is no resumed activity, and the top
// activity is paused, well that is the state we want.
if ((mService.mSleeping || mService.mShuttingDown)
&& mLastPausedActivity == next && next.state == ActivityState.PAUSED) {
......
}
現(xiàn)在我們已經(jīng)準(zhǔn)備好了新的 ActivityStack 和新的 ActivityRecord, 然后我們就需要處理 launcher Activity 的生命周期琐谤,將其置為 pause 狀態(tài)。根據(jù)源碼玩敏,這里首先會(huì)檢查被啟動(dòng)的 Activity 是否在棧頂斗忌,是否就是啟動(dòng)者,如果是的話旺聚,就什么都不做织阳,如果都不滿足的話,就需要將目前棧頂?shù)?Activity 置為 Pause 狀態(tài)砰粹,在這之前唧躲,首先要檢查是否有正在 Pausing 的 Activity, 如果有的話launcher Activity 需要被掛起,
總結(jié)
由于 Activity 啟動(dòng)流程較復(fù)雜碱璃,剩余流程將不再詳述弄痹,總結(jié)如下圖所示: