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 启动流程图

参考文献

相关文章
相关标签/搜索