Activity 启动流程学习总结(附源码)

栏目: IOS · Android · 发布时间: 4年前

内容简介:关于 Activity 启动,Android 中场景大致有两个:本文主要介绍第一种场景由于 Activity 的启动流程中涉及了大量的进程间通信,例如:ActivityManagerService 和 ActivityStack 位于同一进程,ApplicationThread 和 ActivityThread 位于同一进程。所以有必要梳理一下进程与线程的区别

关于 Activity 启动,Android 中场景大致有两个:

  1. 从 launcher 中启动应用,触发该应用默认 Activity 的启动。这种 Activity 都是在新进程和新的任务栈中启动的,所以涉及到新进程和新任务栈的初始化
  2. 应用程序内部启动非默认 Activity, 被启动的 Activity 一般在原来的进程和任务栈中启动

本文主要介绍第一种场景

背景知识

进程与线程

由于 Activity 的启动流程中涉及了大量的进程间通信,例如:ActivityManagerService 和 ActivityStack 位于同一进程,ApplicationThread 和 ActivityThread 位于同一进程。所以有必要梳理一下进程与线程的区别

从操作系统的角度看,进程和线程有什么区别?

  1. 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程造成影响。
  2. 线程没有独立的地址空间,线程只是进程所属进程的不同执行路径

从 JVM 的运行时数据分区的角度,进程和线程有什么关系?

JVM 运行时数据分区如图所示:

Activity 启动流程学习总结(附源码)

由图可以看出如下几点:

  1. 一个进程中的多个线程共享堆区和方法区
  2. 每个线程拥有自己的程序计数器,虚拟机栈,本地方法栈

Activity 启动模式

启动模式横向对比

四中启动模式的概念就不详述了,这里只是对关键点做出横向对比,如图所示

Activity 启动流程学习总结(附源码)

一些鲜为人知的 Intent Flag

下面是一些 Intent Flag 及介绍,如图所示

Activity 启动流程学习总结(附源码)

Activity 启动流程源码分析

概念

首先介绍一下 Activity 启动流程涉及到的核心类

ActivityThread

ActivityThread 的作用是管理应用程序主线程的相关流程,例如管理与处理 activity manager 发送的请求,这些请求可以来自 Activity 或 BroadCast

Instrumentation

它用来监控应用程序与系统之间的交互

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 需要被挂起。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

创新公司

创新公司

[美]艾德·卡特姆、埃米·华莱士 / 靳婷婷 / 中信出版社 / 2015-2 / 49.00元

●《玩具总动员》《海底总动员》《机器人瓦力》《飞屋环游记》等14部脍炙人口的动画长片, 近30次奥斯卡奖, 7部奥斯卡最佳动画长片,7次金球奖; ●几乎每一部电影一上映都位居票房榜首,所有电影都曾进入影史票房总榜前50,每一部电影都是商业与艺术的双赢。 ●即便新兴动画公司不断涌现,皮克斯始终保持动画界的王者之位,这一切背后的秘密就在于:不断推动创新的创意管理方式。 你可以从本书......一起来看看 《创新公司》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具