Activity 啓動流程學習總結(附源碼流程圖)

前言

關於 Activity 啓動,Android 中場景大體有兩個:java

  1. 從 launcher 中啓動應用,觸發該應用默認 Activity 的啓動。這種 Activity 都是在新進程和新的任務棧中啓動的,因此涉及到新進程和新任務棧的初始化
  2. 應用程序內部啓動非默認 Activity, 被啓動的 Activity 通常在原來的進程和任務棧中啓動

本文主要介紹第一種場景git

背景知識

進程與線程

因爲 Activity 的啓動流程中涉及了大量的進程間通訊,例如:ActivityManagerService 和 ActivityStack 位於同一進程,ApplicationThread 和 ActivityThread 位於同一進程。因此有必要梳理一下進程與線程的區別app

從操做系統的角度看,進程和線程有什麼區別?

  1. 進程有獨立的地址空間,一個進程崩潰後,在保護模式下不會對其餘進程形成影響。
  2. 線程沒有獨立的地址空間,線程只是進程所屬進程的不一樣執行路徑

從 JVM 的運行時數據分區的角度,進程和線程有什麼關係?

JVM 運行時數據分區如圖所示:ide

由圖能夠看出以下幾點:

  1. 一個進程中的多個線程共享堆區和方法區
  2. 每一個線程擁有本身的程序計數器,虛擬機棧,本地方法棧

Activity 啓動模式

啓動模式橫向對比

四中啓動模式的概念就不詳述了,這裏只是對關鍵點作出橫向對比,如圖所示源碼分析

Activity 啓動模式比較

一些不爲人知的 Intent Flag

下面是一些 Intent Flag 及介紹,如圖所示this

不爲人知但經常使用的啓動模式 flag

Activity 啓動流程源碼分析

概念

首先介紹一下 Activity 啓動流程涉及到的核心類spa

ActivityThread

ActivityThread 的做用是管理應用程序主線程的相關流程,例如管理與處理 activity manager 發送的請求,這些請求能夠來自 Activity 或 BroadCast操作系統

Instrumentation

它用來監控應用程序與系統之間的交互.net

ActivityManagerService (AMS)

AMS(ActivityManagerService)是貫穿Android系統組件的核心服務,負責了系統中四大組件的啓動、切換、調度以及應用進程管理和調度工做線程

執行流程

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 程序在監聽點擊事件時會判斷頁面是否正在滑動,若是在滑動則不響應點擊應用程序 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 的事件

Step 2. Activity.startActivity()

因爲 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 不須要獲取結果

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 {
			......
		}
 
 
	......
複製代碼

這裏有兩個有趣的場景:

  1. startActivityForResult() 不能用於啓動 singleTask 爲啓動模式的 Activity, 不然會從 onActivityForResult() 中收到 cancel 事件
  2. 假設有這樣一個場景,Activity A 啓動 Activity B, 若是在 A 的 onCreate() / onResume() 中調用 startActivityForResult(), 而且傳入的 requestCode >= 1, 那麼 A Activity 所掛鉤的 Window 將暫時不可見,這種不可見狀態直到收到 B Activity 的回調結果。目的是防止 Activity 跳轉時 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 啓動 Activity, 所以 當前的前臺任務棧棧頂是 launcher Activity, 所以須要建立一個新的 ActivityStack, 和 ActivityRecord 對象,將 ActivityRecord 放入其中,最終這個新建立的 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) {
	......
    }
複製代碼

如今咱們已經準備好了新的 ActivityStack 和新的 ActivityRecord, 而後咱們就須要處理 launcher Activity 的生命週期,將其置爲 pause 狀態。根據源碼,這裏首先會檢查被啓動的 Activity 是否在棧頂,是否就是啓動者,若是是的話,就什麼都不作,若是都不知足的話,就須要將目前棧頂的 Activity 置爲 Pause 狀態,在這以前,首先要檢查是否有正在 Pausing 的 Activity, 若是有的話launcher Activity 須要被掛起。

總結

因爲 Activity 啓動流程較複雜,剩餘流程將再也不詳述,總結以下圖所示:

Activity 啓動流程圖

參考文獻

相關文章
相關標籤/搜索