Android---StartActivity启动过程

在手机桌面应用中点击某一个 icon 之后,最终是通过 startActivity 去打开某一个 Activity 页面。我们知道,Android 中的一个 APP 就相当于一个进程。所以,startActivity 操作中还需要判断,目标 Activity 的进程是否已经创建。如果没有,则在显示 Activity 之前还需要将进程 Process 提前创建出来。

假设是从 Activity A 跳转到另一个 APP 中的 Activity B。过程如下图所示

整个 startActivity 的流程分为 3 大部分,也涉及 3 个进程之间的交互:

\bullet ActivityA --> ActivityManagerService(简称 AMS)

\bullet ActivityManagerService --> ApplicationThrad

\bullet ApplicationThread --> Activity

1. ActivityA 到 AMS 阶段

这一过程并不复杂,用如下这张图表示具体过程

接下来查看源码,看看做了哪些操作

Activity 的 startActivity(),最终调用了 startActivityForResult() 方法。传入的 -1 表示不需要获取 startActivity 的结果。

Activity 的 startActivityForResult(),具体代码如下所示。调用 Instrumentation 的 execStartActivity方法,剩下的交给 Instrumentation 类去处理。

Instrumentation 类主要用来监控应用程序与系统交互。蓝线部分的 MainThread 是 ActivityThread 类型。ActivityThread 可用理解为1个进程,这里即表示 ActivityA 所在的进程。通过 MainThread 获取一个 ApplicationThread 引用,这个引用就是实现进程间通信的。具体来说,就是 AMS 所在的系统进程通知应用进程进行了一系列操作。

Instrumentation 类的 execStartActivity。在 instrumentation 中会通过 ActivityManager.getService获取 AMS 的实例。然后,调用其 startActivity 方法。实际上这里就是通过 AIDL 来调用 AMS 的 startActivity 方法。

至此,startActivity 的工作重心成功的从进程 A 转移到了系统进程 AMS中。

2. ActivityManagerService 到 ApplicationThread 阶段

下面来探索在 AMS 中是如何一步一步执行到 B 进程的。

上面在讲 Instrumentation 时,我们提到了一个 ApplicationThread 类来负责进程间通信的。这里 AMS 最终其实就是调用了 B 进程的一个 ApplicationThread 引用,从而间接地通知 B 进程进行相应操作。

相比较 startActivity 到 AMS,AMS 到 ApplicationThread 的流程看起来复杂了很多。实际上,这里面就干了两件事情:

a):综合处理 launchMode 和 Intent 中的 Flag 标志位,并根据处理结果生成一个目标 Activity B 的对象(ActivityRecord);

b):判断是否需要为目标 Activity B 创建一个新的进程(ProcessRecord)、新的任务栈(TaskRecord)

AMS 的 startActivity

从上图中的代码可以看出,经过多个方法的调用,最终通过 obtainStarter() 方法获取了 ActivityStarter 类型的对象,然后调用其 execute() 方法。在 execute() 方法中,会再次调用其内部的 startActivityMayWait 方法。

ActivityStarter 的 startActivityMayWait 

ActivityStarter 类专门负责一个 Activity 的启动操作主要作用包括解析 Intent、创建 ActivityRecord、如果有可能还要创建 TaskRecord。

startActivityMayWait 的部分实现代码如下

从上面代码可以看出,获取目标 Activity 信息的操作由 mSupervisor 来实现,它是 ActivityStackSupervisor 类型。主要是负责 Activity 所处栈的管理类。在上面代码的 resolveIntent 中,实际上是调用系统 PackageManagerService 来获取最佳 Activity。有时候,我们通过隐式 Intent 启动 Activity 时,系统中可能存在多个 Activity 可以处理 Intent。此时会弹出一个选择框让用户选择具体需要打开哪一个 Activity 界面,就是此处的逻辑处理结果。

在 startActivityMayWait 方法中,调用了一个重载的 startActivity 方法,最终会调用 ActivityStarter 中的 startActivityUnchecked 方法来获取启动 Activity 的结果。

ActivityStarter 的 startActivityUnchecked

图中 1 处计算启动 Activity 的 Flag 值;图中2处处理 Task 和 Activity 的进栈操作;图中3处启动栈中顶部的 Activity。

\bullet computeLaunchingTaskFlags 方法

这个方法的主要作用是计算启动 Activity 的 Flag,不同的 Flag 决定了启动 Activity 最终会被放置到哪一个 Task 集合中。

图中1处 mInTask 是 TaskRecord 类型,此处为 null,代表 Activity 要加入的栈不存在。因此需要判断是否需要新建 Task;图中2处的 mSourceRecord 是 ActivityRecord 类型,它是用来描述初始 Activity(比如,ActivityA 启动了 ActivityB,ActivityA 就是初始 Activity)。当我们使用 Context 或者 Application 启动 Activity 时,此时 mSourceRecord 为 null;图中3处表示初始 Activity,如果是在 LAUNCH_SINGLE_INSTANCE 栈中的 Activity,需要添加 FLAG_ACTIVITY_NEW_TASK 的标识。因为 LAUNCH_SINGLE_INSTANCE 栈只能允许保存一个 Activity;图中4处表示,如果 LaunchMode 设置了 LAUNCH_SINGLE_TASK 或 LAUNCH_SINGLE_INSTANCE,则也要创建一个新栈。

\bullet startActivityLocked 方法

方法中会调用 insertTaskAtTop() 方法,尝试将 Task 和 Activity 入栈。如果 Activity 是以 NEW_TASK 的模式启动或者 Task 堆栈中不存在该 TaskId,则 task 会重新入栈并且放在栈的顶部。注意:Task 先入栈,之后才是 Activity 入栈,它们是包含关系。

上面一下子引出了好几个概念:Stack,Task,Activity。其实它们都是在 AMS 内部维护的数据结构,它们之间的关系如下图

\bullet  resumeFocusedStackTopActivityLocked 方法

经过一些列调用,最终代码又回到了 ActivitSupervisor 中

ActivityStackSupervisor 的 startSpecificActivityLocked 方法

图中1处根据进程名称和 Application 的 UID,来判断目标进程是否已经创建。如果没有,则代表进程未创建;图中2处调用 AMS 创建 Activity 所在进程。

不管是目标进程已经存在还是新建目标进程,最终都会调用图中红线部分的 realStartActivityLocked 方法来执行启动 Activity 的操作。

ActivityStackSupervisor 的 realStartActivityLocked 方法

这个方法在 Android 27 和 28两个版本的区别很大。从 android-28 开始 Activity 的启动交给了事务(Transaction)来完成。图中1处创建了 Activity 启动事务,并传入 app.thread 参数,它是 applicationThread 类型。在上文 startActivity 阶段,applicationThread 是为例实现进程间通信的,是 ActivityThread 的一个内部类;图中2处执行 Activity 的启动事务,由 ClientLifecycleManager 来完成。具体代码如下

可以看出,实际上是调用了启动事务的 ClientTransaction 的 schedule() 方法。而这个 transaction 实际上是在创建 ClientTransaction 时,传入的 app.thread 对象,也就是 applicationThrad。

这里传入的 app.thread 会赋值给 ClientTransaction 的成员变量 mClient,ClientTransaction 会调用 mClient.scheduleTransaction(this) 来执行事务。这个 app.thread 是 ActivityThread 的内部类 ApplicationThread,所以事务最终是调用 app.thread 的 scheduleTransaction 执行

 到这里位置,startActivity 的操作就成功的从 AMS 转移到了另一个进程B 中的 ApplicationThread 中。剩下的就是 AMS 通过进程间通信机制通知 ApplicationThread 执行 ActivityB 的生命周期方法。

3. ApplicationThread 到 Activity 阶段

上面我们分析到,AMS 将启动 Activity 的任务作为一个事务 ClientTransaction 去完成。在 ClientLifecycleManager 中会调用 ClientTransaction 的 schedule() 方法。如下代码所示

mClient 是一个 IApplicationThread 接口类型,具体实现是 ActivityThread 的内部类 ApplicationThread。因此,后续执行 Activity 生命周期的过程都是由 ApplicationThread 指导完成的。

上面代码中的 scheduleTransaction() 方法仍然是调用了 ActivityThread 的 scheduleTransaction() 方法,但实际上这个方法是在 ActivityThread 的父类 ClientTransactionHandler 中实现。

 调用 sendMessage() 方法向 Hanlder 中发送了一个 EXECUTE_TRANSACTION 消息,并且 Message 中的 obj 就是启动 Activity 的事务对象。而这个 Handler 的具体实现是 ActivityThread 中的 mH 对象。具体代码如下

最终调用了事务的 execute() 方法,如下代码所示

在 executeCallback 方法中,会遍历事务中的 callback 并执行 execute 方法。这些 callbacks 是何时被添加的呢?

前面在创建 ClientTransaction 时,通过 addCallback() 方法传入了callback 参数。从下面代码可以看出,其实是一个 LaunchActivityItem 对象

LaunchActivityItem 的 execute() 方法

这里就已经到了与 Activity 生命周期相关的方法。上面代码中的 client 是 ClientTransactionHandler 类型,实际实现类是 ActivityThread,因此最终方法又回到了 ActivityThread。

ActivityThread 的 handleLaunchActivity

这是一个比较重要的方法,Activity 的生命周期方法就是在这个方法中有序执行。

图中1处,初始化 Activity 的 WindowManager,每一个 Activity 都会对应一个窗口;图中2处,调用 performLaunchActivity 创建并显示 Activity;图中3处通过反射创建目标 Activity 对象;图中4处调用 attach 方法建立 activity 与 context 之间的联系。创建 phoneWindow 对象,并与 Activity 进行关联操作;图中5处通过 instrumentation 最终调用 Activity 的 onCreate 方法。

至此,目标 Activity 已经成功创建,并执行生命周期方法。

总结

本次详细介绍了 Activity 的启动在源码中的实现流程

这一过程主要涉及 3 个进程间的通信过程:

\bullet 进程 A 通过 Binder 调用 AMS 的 startActivity 方法;

\bullet AMS 通过一系列的计算构造目标 Intent,然后在 ActivityStack 与 ActivityStackSupervisor 中处理 Task 和 Activity 的入栈操作;

\bullet AMS 通过 Binder 机制,调用目标进程中 ApplicationThread 的方法来创建并执行 Activity 生命周期方法,ApplicationThread 是 ActivityThread 的一个内部类,最终都调用到了 ActivityThread 中的相应方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/171354.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

米尔AM62x核心板助力新一代工业4.0升级

米尔AM62x核心板 续写AM335x经典 在过去的十几年中,TI Sitara系列推出了很多优秀的处理器,其中在工业、电力、医疗等领域有着广泛应用的AM335x系列处理器,引领工业市场从MCU向MPU演进,帮助产业界从ARM9迅速迁移至高性能Cortex-A…

Spring Authorization Server 1.1 扩展实现 OAuth2 密码模式与 Spring Cloud 的整合实战

目录 前言无图无真相创建数据库授权服务器maven 依赖application.yml授权服务器配置AuthorizationServierConfigDefaultSecutiryConfig 密码模式扩展PasswordAuthenticationTokenPasswordAuthenticationConverterPasswordAuthenticationProvider JWT 自定义字段自定义认证响应认…

【ARM Trace32(劳特巴赫) 使用介绍 2 -- Trace32 cmm 脚本基本语法及常用命令】

文章目录 Trace32 CMM 概述1.1 Trace32 系统命令 SYStem1.1.1 Trace32 SYStem.CONFIG1.1.2 SYStem.MemAccess1.1.3 SYStem.Mode1.1.3.1 TRST-Resets the JTAG TAP controller and the CPU internal debug logic1.1.3.2 SRST- Resets the CPU core and peripherals 1.2 Trace32 …

【Linux】解决缓存锁问题:无法获得锁 /var/lib/dpkg/lock-frontend

今天在运行apt-get update更新软件包后,突然发现安装新的软件出现了这个报错:正在等待缓存锁:无法获得锁 /var/lib/dpkg/lock-frontend。锁正由进程 1855(unattended-upgr)持有。如图。 这个错误通常是由于其他进程正在…

“从部署到优化,打造高效会议管理系统“

目录 引言一、部署单机项目 - 会议OA1.1 硬件和软件环境准备1.2 检查项目1.3 系统部署1.后端部署 二、部署前后端分离项目 - SPA项目后端部署2.前端部署 总结 引言 在现代化办公环境中,会议是组织沟通、决策和合作的重要方式之一。为了提高会议的效率和质量&#x…

Win11安装ise14.7~不需要虚拟机了~

之前一直无法在win11上安装ise14.7,网上搜索也无果,所有一直vmware虚拟机使用。直到最近看了水木上jesce的回复,试了下果然可以直接安装使用的。 步骤如下即可: 1.安装时切勿勾选最后一项,Enable WebTalk to send so…

2023 10月最新Vmd 下载安装教程,WindowsLinux

文章目录 下载Vmdwindows版本安装LINUX版本安装 下载Vmd 谷歌搜索VMD 点击左下角download VMD 可选择对应版本 注:点击后会出现输入用户名和密码,由于我已注册,界面不见了,所以直接描述一下。 输入用户名和密码然后会出现让登记…

SLAM从入门到精通(lidar的运动畸变矫正)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 前面我们说过,很多时候传感器的数据并不能直接拿过来使用。这里面除了噪声的原因之外,另外一部分原因就是传感器数据本身也…

算法通关村第三关|青铜|线性表数组热身

1.线性表 1.1 线性表 线性表就是具有相同特征数据元素的一个有限序列。 1.2 数组存储元素的初始化 数组初始化时必须对从前向后的连续空间初始化,不可以出现空缺的情况。写 0 也是初始化,也要从前向后写。 2.热身-单调数组 判断是否为单调数组&am…

设置Ubuntu 20.04的静态IP地址(wifi模式下)

一、引言 自己家用的Ubuntu的,重启后ip地址经常会改变,这个时候就需要我们手动配置静态IP了。 二、优点 给Ubuntu设置一个静态IP地址有以下几个好处: 持久性:静态IP地址是固定不变的,与设备的MAC地址绑定。这意味着…

一、【Photoshop如何根据不同类型图像抠图】

文章目录 前言图形结构1、规则图形2、不规则图形 图形颜色1、轮廓清晰2、颜色分明 前言 当我们有抠图需求的时候,不要一开始就想着我怎么去把它抠出来,首先应该分析图形的特点,然后再去选取合适的工具,这样才可以做到事半功倍&am…

经典卷积神经网络 - NIN

网络中的网络,NIN。 AlexNet和VGG都是先由卷积层构成的模块充分抽取空间特征,再由全连接层构成的模块来输出分类结果。但是其中的全连接层的参数量过于巨大,因此NiN提出用1*1卷积代替全连接层,串联多个由卷积层和“全连接”层构成…

电脑定时关机

电脑定时关机 1.右键 管理 2. 3. 4. 5. shutdown.exe/s /f /t 06.点击完成就好了 7.这里面可以 看到定时任务和启动 右键有运行 结束 禁用

Flask 上传文件,requests通过接口上传文件

这是一个使用 Flask 框架实现文件上传功能的示例代码。该代码定义了两个路由: /upload:处理文件上传请求。在该路由中,我们首先从请求中获取上传的文件,然后将文件保存到本地磁盘上,并返回一个字符串表示上传成功。 /…

LLM系列 | 22 : Code Llama实战(下篇):本地部署、量化及GPT-4对比

引言 模型简介 依赖安装 模型inference 代码补全 4-bit版模型 代码填充 指令编码 Code Llama vs ChatGPT vs GPT4 小结 引言 青山隐隐水迢迢,秋尽江南草未凋。 小伙伴们好,我是《小窗幽记机器学习》的小编:卖热干面的小女孩。紧接…

记录--vue3实现excel文件预览和打印

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 前言 在前端开发中,有时候一些业务场景中,我们有需求要去实现excel的预览和打印功能,本文在vue3中如何实现Excel文件的预览和打印。 预览excel 关于实现excel文档在…

android——自定义控件(编辑框)、悬浮窗

一、自定义编辑框 效果图: 主要的代码为: class EditLayout JvmOverloads constructor(context: Context, attrs: AttributeSet? null, defStyleAttr: Int 0 ) : ConstraintLayout(context, attrs, defStyleAttr) {private var editTitle: Stringpr…

五、W5100S/W5500+RP2040树莓派Pico<UDP Client数据回环测试>

文章目录 1. 前言2. 协议简介2.1 简述2.2 优点2.3 应用 3. WIZnet以太网芯片4. UDP Client回环测试4.1 程序流程图4.2 测试准备4.3 连接方式4.4 相关代码4.5 测试现象 5. 注意事项6. 相关链接 1. 前言 UDP是一种无连接的网络协议,它提供了一种简单的、不可靠的方式来…

线框图软件:Balsamiq Wireframes mac中文介绍

Balsamiq Wireframes mac是一款用于创建线框图的软件工具。它旨在帮助用户快速制作出清晰、简洁的界面原型,以便在设计和开发过程中进行协作和沟通。 Balsamiq Wireframes具有简单直观的用户界面,使用户能够快速添加和编辑各种用户界面元素,如…

Java采集传感器数据,亲测有效!

背景 先说背景, 最近公司项目需要用到传感器,采集设备温湿度,倾斜角,电流…,公司采购采购了一个温湿度传感器给我们开发测试使用,如下图: 看着还挺精致有没有。 进入正题 有了这个温湿度传感器…