内容简介:关于 Activity 启动,Android 中场景大致有两个:本文主要介绍第一种场景由于 Activity 的启动流程中涉及了大量的进程间通信,例如:ActivityManagerService 和 ActivityStack 位于同一进程,ApplicationThread 和 ActivityThread 位于同一进程。所以有必要梳理一下进程与线程的区别
关于 Activity 启动,Android 中场景大致有两个:
- 从 launcher 中启动应用,触发该应用默认 Activity 的启动。这种 Activity 都是在新进程和新的任务栈中启动的,所以涉及到新进程和新任务栈的初始化
- 应用程序内部启动非默认 Activity, 被启动的 Activity 一般在原来的进程和任务栈中启动
本文主要介绍第一种场景
背景知识
进程与线程
由于 Activity 的启动流程中涉及了大量的进程间通信,例如:ActivityManagerService 和 ActivityStack 位于同一进程,ApplicationThread 和 ActivityThread 位于同一进程。所以有必要梳理一下进程与线程的区别
从操作系统的角度看,进程和线程有什么区别?
- 进程有独立的地址空间,一个进程崩溃后,在保护模式下不会对其他进程造成影响。
- 线程没有独立的地址空间,线程只是进程所属进程的不同执行路径
从 JVM 的运行时数据分区的角度,进程和线程有什么关系?
JVM 运行时数据分区如图所示:
由图可以看出如下几点:
- 一个进程中的多个线程共享堆区和方法区
- 每个线程拥有自己的程序计数器,虚拟机栈,本地方法栈
Activity 启动模式
启动模式横向对比
四中启动模式的概念就不详述了,这里只是对关键点做出横向对比,如图所示
一些鲜为人知的 Intent Flag
下面是一些 Intent Flag 及介绍,如图所示
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 { ...... } ...... 复制代码
这里有两个有趣的场景:
-
startActivityForResult()
不能用于启动singleTask
为启动模式的 Activity, 否则会从onActivityForResult()
中收到 cancel 事件 - 假设有这样一个场景,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 需要被挂起。
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- kubelet 分析源码:启动流程
- 【zookeeper源码】启动流程详解
- View绘制流程源码分析
- gorm查询流程源码分析
- ReactNative源码解析-启动流程
- Android 系统源码-1:Android 系统启动流程源码分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
维多利亚时代的互联网
[英] 汤姆·斯丹迪奇 / 多绥婷 / 后浪丨江西人民出版社 / 2017-8 / 38.00元
人类历史上的第一次大连接 回顾互联网的前世 预言互联网的未来 ……………… ※编辑推荐※ ☆《财富》杂志推荐的75本商务人士必读书之一 ☆ 回顾互联网的前世,颠覆你的思维,升级你对互联网的认知 ☆ 人类历史上一次全球大连接是维多利亚时期的电报时代,那时候也有疯狂的资本、 巨大的泡沫、网络新型犯罪、网络亚文化崛起……现在的互联网时代就是电报时代的重演;回顾那......一起来看看 《维多利亚时代的互联网》 这本书的介绍吧!