關於 Activity 啓動,Android 中場景大體有兩個:java
本文主要介紹第一種場景git
因爲 Activity 的啓動流程中涉及了大量的進程間通訊,例如:ActivityManagerService 和 ActivityStack 位於同一進程,ApplicationThread 和 ActivityThread 位於同一進程。因此有必要梳理一下進程與線程的區別app
JVM 運行時數據分區如圖所示:ide
四中啓動模式的概念就不詳述了,這裏只是對關鍵點作出橫向對比,如圖所示源碼分析
下面是一些 Intent Flag 及介紹,如圖所示this
首先介紹一下 Activity 啓動流程涉及到的核心類spa
ActivityThread 的做用是管理應用程序主線程的相關流程,例如管理與處理 activity manager 發送的請求,這些請求能夠來自 Activity 或 BroadCast操作系統
它用來監控應用程序與系統之間的交互.net
AMS(ActivityManagerService)是貫穿Android系統組件的核心服務,負責了系統中四大組件的啓動、切換、調度以及應用進程管理和調度工做線程
/** * 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 程序在監聽點擊事件時會判斷頁面是否正在滑動,若是在滑動則不響應點擊應用程序 icon 的事件
return;
}
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) { // 處理點擊應用程序 icon 的場景
// 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 這裏主要會判斷待啓動的 Activity 是否存在,若不存在則會報 ActivityNotFound Exception 並捕獲
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
}
} else if (tag instanceof FolderInfo) { // 處理點擊文件夾的場景
···
} else if (v == mAllAppsButton) {
···
}
}
複製代碼
Launcher 程序在監聽點擊事件時會判斷頁面是否正在滑動,若是在滑動則不響應點擊應用程序 icon 的事件
因爲 Launcher 繼承於 Activity, 所以代碼執行流程到了 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 最終是調用 startActivityForResult
, 傳入的參數爲 -1 表明此次啓動 Activity 不須要獲取結果
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 {
......
}
......
複製代碼
這裏有兩個有趣的場景:
startActivityForResult()
不能用於啓動 singleTask
爲啓動模式的 Activity, 不然會從 onActivityForResult()
中收到 cancel 事件startActivityForResult()
, 而且傳入的 requestCode >= 1, 那麼 A Activity 所掛鉤的 Window 將暫時不可見,這種不可見狀態直到收到 B Activity 的回調結果。目的是防止 Activity 跳轉時 UI 閃爍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 啓動 Activity, 所以 當前的前臺任務棧棧頂是 launcher Activity, 所以須要建立一個新的 ActivityStack, 和 ActivityRecord 對象,將 ActivityRecord 放入其中,最終這個新建立的 ActivityStack 被保存到 AMS 中
// 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) {
......
}
複製代碼
如今咱們已經準備好了新的 ActivityStack 和新的 ActivityRecord, 而後咱們就須要處理 launcher Activity 的生命週期,將其置爲 pause 狀態。根據源碼,這裏首先會檢查被啓動的 Activity 是否在棧頂,是否就是啓動者,若是是的話,就什麼都不作,若是都不知足的話,就須要將目前棧頂的 Activity 置爲 Pause 狀態,在這以前,首先要檢查是否有正在 Pausing 的 Activity, 若是有的話launcher Activity 須要被掛起。
因爲 Activity 啓動流程較複雜,剩餘流程將再也不詳述,總結以下圖所示: