Laucher中activtyA启动APP进程中的ActivityB

第一步:解析要启动的Activity信息

Launcher通过Instrumentation类,在Instrumentation中通过ServiceManager拿到AMS的Binder和AMS通信,调用AMS里面的startActivity方法,同时把Launcher进程的包名和所对应的ApplicationThread(Binder),intent里面所包含的信息全部传递给AMS的ActivityStart里面的静态的Request。

ActivityStart这个类专门用于解析Activity信息,配置activity关联的任务和堆栈,里面有一个静态的Request类,这个类里面的参数包含了启动activityA的相关信息和被启动activityB的相关信息。

static class Request {
...
IApplicationThread caller;//Launcher的Binder,也代表着谁来调用我们的启动流程
Intent intent;//startActivity里面传入的Intent
NeededUriGrants intentGrants;
// A copy of the original requested intent, in case for ephemeral app launch.
Intent ephemeralIntent;
String resolvedType;
ActivityInfo activityInfo;
ResolveInfo resolveInfo;
IVoiceInteractionSession voiceSession;
IVoiceInteractor voiceInteractor;
IBinder resultTo;
String resultWho;
int requestCode;//activity启动的返回结果
...
}

AMS创建和管理ActivityStart是通过ActivityStartController。AMS每次启动一个Activity都会需要创建一个ActivityStart类来管理Activity的启动信息。在App中启动Activity是一件非常频繁的事情,如果每一次启动Activity都重新创建ActivityStart对象的话,当相关方法执行完毕,ActivityStart对象会被回收。频繁的创建和频繁的回收就势必会造成内存抖动。我们在JVM的新生代使用的垃圾回收算法是复制清除算法,在老年代是标志清除算法或者标记整理清除算法,除了标记清除算法外,其余两种都会造成对象的移动,除了垃圾回收线程外的其余所有线程都必须暂停,会造成一定的卡顿。所以在AMS中并不是直接去创建ActivityStart对象的,通过ActivityStartController去创建和管理ActivityStart对象。使用了享元设计模式。当相关方法执行完毕,把ActivityStart对象先清除里面所有的数据,然后放置在被final修饰(final修饰的变量存在在常量池,是一个GC root)的一个对象的里面的池子里面,池子的容量是3。每次创建先从池子里面拿,拿不到再创建ActivityStart对象,用完后抹除对象里面所有的数据放回到池子里面。这样做避免了内存抖动。和Message的创建是一样的。

ActivityStartController创建完ActivityStart后,把Launcher的包名,Launcher的ApplicationThread和intent里面所有的信息存储到ActivityStart的Request类里面。然后在ActivityStart里面创建两个ActivityRecord,这个ActivityRecord我理解为Activity在AMS里面的一种体现吧,我们的Activity目前还没创建,真正创建实在ActivityThread的handLaunchActivty里面,在创建Activity之前,AMS怎么对Activity操作呢?就弄了一个ActivityRecord来代表Activity。ActivityRecord里面就包含了一个Activity的所有信息,比如说Activity所处的栈,处于哪个栈。
ActivityStart一共干了一下几种事情:

  1. 存储Launcher所有的App的binder,解析intent里面的参数,也就是ActivityA和ActivityB的相关参数,存储到内部的静态类Request里面
  2. 把ActivityA和ActivityB在AMS端的代表,ActivityRecord创建出来,ActivityRecord里面包含了Activity的相关信息,其中就包括Activity的相关得的栈信息。
  3. 启动之前还没来得及启动的Activity,我的理解是一个屏幕内同时启动两个Activity就是在这里进行处理的,确保启动一个的时候,另外一个Activity也会被启动。
  4. 初始化启动模式,为ActivityRecord存储的启动模式赋值,然后将相关启动模式塞到Intent里面
  5. 创建一个启动时的黑白屏。因为这个时候我们的APP进程还没有被启动,而App进程启动的过程是很缓慢的,可能需要个2秒,但是如果不给这个黑白屏的话,在这两秒之内,用户会以为点击没效果,或者认为自己没有点中app图标,会疯狂点击。先给一个黑白屏让用户知道操作系统已经接受到他的点击事件了。
  6. 把后续的操作交给RootWindowContainer去执行。RootWindowContainer是窗口容器的根容器,管理所有的窗口容器。窗口是需要基于Activity来显示的,后续的操作在resumeFocusedStacksTopActivitys里面进行,个人理解是恢复目前栈的栈顶Activity,可见性和窗口脱不了关系。所以谷歌工程师可能想通过RootWindowContainer来控制Activity的后续流程。

第二步:ActivityStackSupervisor启动Activity

ActivityRecord:一个ActivityRecord对应着一个Activity,保存一个Activity的所有信息;但是一个Activity可能对应着多个ActivityRecord,这与Activity的启动模式有关,一个Activity可能会被启动多次。

Task:Android系统中的每个Activity都位于一个Task中。一个Task能够包括多个Activity,同一个Activity也可能有多
个实例。 在AndroidManifest.xml中,我们能够通过 android:launchMode 来控制Activity在Task中的实例。Task管
理的意义还在于近期任务列表以及Back栈。 当你通过多任务键(有些设备上是长按Home键。有些设备上是专门提
供的多任务键)调出多任务时,事实上就是从ActivityManagerService获取了近期启动的Task列表。

ActivityStack:Task是它的父类,是一个管理类,管理着所有 Activity ,内部维护了 Activity 的所有状态、特
殊状态的Actvity和Activity以及相关的列表数据。

ActivityStackSupervisor: ActivityStack 是由 ActivityStackSupervisor 来进行管理,而这个是在ATMS的
initialize 中被创建出来的。ATMS初始化的时候,会创建一个ActivityStackSupervisor对象用于统一管理一些事
务:1)将显示层次结构相关的内容移动到RootWindowContainer中;2)将与activity生命周期相关的事务的处理转
交给ActivityLifeCyler处理;3)处理点击物理按键Menu键后任务栈的处理。

RootWindowContainer通过拿到ActivityStack执行ActivtyA的onPase方法,之后把后续流程交给ActivityStackSupervisor来处理。ActivityStackSupervisor是ActivityStack的大管家,管理着所有的ActivityStack。在ActivityStackSupervisor里面会判断当前要启动的Activity所在的进程有没有被创建,如果没有被创建的话。会通过ProcessList去拿到对应的RrocessRecord。

ProcessRecord记录App进程相关信息的重要类,AMS管理所有App进程,每一个App进程都对应着一个ProcessRecord类,里面存储着App进程的所有信息。AMS为了管理这些进程信息,创建了一个ProcessList,通过ProcessList来管理这些ProcessRecord。
ProcessList里面会有一个列表存放这最近使用的进程所对应的ProcessReocrd。

ActivityStackSupervisor里面会为当前进程创建一个RrocessRecord,通过ProcessList对ProcessRecord进行管理。然后通过ProcessList调用Process工具类,通过Process工具类去调用ZygoteProcess的start方法,通过socket发送消息给到zygotesever。

ZygoteInit进程在被创建出来后,在main方法里面执行了这几件事情:

  1. 加载系统类,加载系统资源,加载一些共享so库,加载字体资源,加载webview相关资源
  2. 创建socket服务,zygoteserver
  3. Fork出的第一个进程 system_server
  4. 在zygoteserver死循环等待socket消息,循环等待fork出其他的应用进程,比如Launcher,比如app

一旦我们通过Launcher和AMS通信,要求启动App进程里面的ActivityB,在ActivityStackSupervior里面判断ActivityB所在的进程没有被启动,则会通过ProcessList创建一个ProcessRecord存储当前要创建的进程的信息,然后再通过对工具类Process的调用进阶对ZygoteProcess的调用发送一条socket信息。这时候在ZygoteServer中的死循环就能检测到这条socket信息。然后获取socket命令,拿到发送过来的要fork的进程的相关信息,最后通过fork操作得到两个pid。之所以是得到两个pid是因为fork操作是把zygote进程完整复制一遍,就像细胞分裂一样。原来在zygote进程里面进行fork操作的地方在我们的子进程里面也会存在,也会返回一个结果。pid如果大于0是父进程Zygote,等于0是子进程,小与0说明执行fork操作失败。fork进程后第一时间把我们子进程的socket关闭,因为zygote进程的socket是进行跨进程通信使用,但是我们子进程跨进程通信使用的是binder。然后初始化运行环境,把我们子进程的binder驱动打开,创建binder线程池,最后通过反射去找到ActivityThread的main方法执行。

拿到socket消息后一共干了这几件事情:

  1. 通过fork操作fork出子进程,fork操作会返回两个pid,因为fork操作是把zygote进程完整复制一遍,就像细胞分裂一样。原来在zygote进程里面进行fork操作的地方在我们的子进程里面也会存在,也会返回一个结果。
  2. 关闭子进程的socket服务,因为ygote进程的socket是进行跨进程通信使用,但是我们子进程跨进程通信使用的是binder
  3. 初始化运行环境
  4. 把子进程的binder驱动打开,创建binder线程池
  5. 反射找到Activity的main方法执行

为什么zygote跨进程通信要使用socket,Linnux里面是不允许在多线程环境下进行fork操作的,fork操作只会把父进程里面发起fork的那个线程复制到子进程里面,而Binder天生就是多线程的,如果有一个线程在等锁的话,那么在子进程里面他将永远在等锁。

为什么要使用反射来执行main方法,拿到socket消息后的所有操作都被封装成了一个runable,最后的runable的run方法就是拿到进程对应的main方法,执行main方法。封装正runable后统一使用反射来调用有点类似泛型,我只需要写一套代码,然后不管是system_service的fork还是activityThread的启动只需要用着一套代码就行了。实现了代码的复用,也方便统一管理。

在ActivityThread里面做了这几件事:

  1. 为我们App进程创建一个Looper对象,并且使用ThreadLocal的set方法保存,赋值为一个static修饰的sMainLooper引用上面
  2. 调用Looper的loop方法,启动一个死循环,不断的从MessageQuene里面取出Message,没有message的时候通过linnux的epoll wait机制让出cpu资源,进行休眠
  3. 通过执行ActivityThread的attach方法,先通过ServiceManage拿到AMS的Binder,然后通过ActivityManager.Stub.asInterface拿到AMS的本地代理,通过和AMS所在的进程System_service进行跨进程通信,把我们App进程的Binder,ApplicationThread传递给AMS。

接下来的事情又回到AMS端了

在System_service进程的SystemServiceManager管理的AMS里面做了这几件事:

  1. 把我们的APP的Binder存到对应的ProcessRecord里面
  2. 把ProcessRecord的顺序在ProcessList重新排序一下
  3. 通过App的Binder调用App端的ApplicationThread的attachApplication,创建一个Application
  4. 回调到ActivityStackSupervisor里面完成剩余的Activity启动流程

通过Binder调用App端创建Application和后面的通过Binder把ClientTranstion传递给App端是通过Binder异步通信。所以有可能存在着Activity创建时,Application还没被创建的问题。会在Activity创建后调用makeApplication确保一下Application是被成功创建的,然后再去执行Activity的相关生命周期。

在ActivityThread里面创建Application做了这几件事情:

  1. ApplicationThread通过sendMessage的方式去切换到主线程,因为AMS和App端跨进程通信使用的是Binder的异步机制,所以ApplicationThread里面的方法都是在异步线程里面执行的,需要通过handler切换到主线程里面。
  2. 通过makeApplication函数,在内部通过反射来创建Application
  3. 执行Appliaction的attach方法,在这个方法里面执行attachBaseContext
  4. 创建contentProvide
  5. 执行Application的onCreate方法

接下来回到Activity的启动流程,我们目前已经解析完了Activity的启动参数,创建出Activity的进程,创建出一个Application了。接下来会回到ActivityStackSupervior里面,先执行一下ActivityA的onPause方法,让ActivityA处于一个可见不可交互状态。然后在ActivityStackSupervior里面,把ActivityB的生命周期封装成一个事务ClientTranstion,在ClientTranstion事务里面保存了AMS希望App端处理的ActivityB的起始生命周期状态LaunchActivityItem,ActivityB的结束生命周期状态ResumeActivityItem。然后通过ClientLifecycleManager对ClientTranstion进行管理,拿到app端的binder,通过跨进程通信,在ClientLifecycleManager里面把当前的ClientTranstion传递给ActivityThread。

ActivityStackSupervisor里面一共干了以下几件事:

  1. 根据当前Activity的所在进程有没有被创建去执行Activity的启动逻辑,没有被创建的话通过ProcessList去创建ProcessRecord,再通过工具类Process去操作ZygoteProcess发送socket信息,在ZygoteServer里面收到信息fork出进程。App进程被fork出来第一时间关掉socket服务,然后初始化运行环境,启动binder驱动和binder线程池,最后通过反射去找打ActivityThread的main方法执行。在main方法里面又调用attach,跨进程通信把app的binder传递给System_service里面的AMS。AMS保存当前的App进程的binder到对应的ProcessRecord里面,并且更新ProcessRecord在ProcessList里面的位置。又通过Binder和App进程通信,跨进程调用bindApplication。在App进程里面先通过sendMessage方法,把相关信息通过handler发送到主线程。在主线程通过makeApplication反射创建一个Application,执行application的attach方法,attachBaseContent方法,再创建contentProvide方法,最后才执行Application的onCreate方法。
  2. 执行ActivityA的onPause方法
  3. 把AMS希望App端执行的ActivityB的起始生命周期LaunchActivityItem,结束生命周期ResumeActivityItem包装成一个ClientTranstion。通过ClientLifecycleManager管理ClientTranstion,在ClientLifecycleManager里面拿到App对应的binder,跨进程通信把事务ClientTranstion传递给App端

ActivityThread对Activity生命周期的管理

在ActivityThread里面接受到AMS传递的生命周期事务ClientTranstion后,通过sendMessage切换到主线程。利用事务处理器TranstioneExector处理事务里面的生命周期,起始生命周期LaunchActivityItem和结束生命周期ResumeActivityItem。在处理LauncherActivityItem方法的时候,首先创建ActivityClientRecord,ActivityClientRecord是Activity在App进程里面的一个实体,里面会存有一个变量Activity。然后会调用handleLaunchActivity方法,把这个创建的ActivityClientRecord传递给它,实际上调用performLaunchActivity反射创建一个Activity,之前说过AMS和App跨进程通信创建Application和AMS和App跨进程通信传输ClientTranstion是使用了Binder的异步通信机制,所以创建Activity后会进行makeApplication,确保在执行activity的onCreate方法前,application已经被成功创建出来。执行完onCreate后,把当前的生命周期状态ON_CREATE存储在ActivityClientRecord里面。接下来事务处理器拿到结束生命周期ResumeActivityItem的生命周期状态ON_RESUME,计算存储在ActivityClientRecord里面的生命周期状态和结束生命周期状态,发现中间还有一个ON_START生命周期,接着执行ON_START生命周期。最后再执行ON_RESUME生命周期。在handleResumeActivity执行完ActivityB的onResume方法后,会通过IdlerHandler的queueIdle方法里面拿到AMS的Binder,然后和AMS通信,跑到ActivityStackSupervisor类里面,找到Activity对应的ActivityRecord,在ActivityRecord里面把onStop包装成一个事务ClientTranstion,然后通过ClientLicycleManager对事物进行管理,拿到对应的App进程的Binder,最后把包装好的事务跨进程通信传递回Launcher进程,使用事务处理器TranstionExector进行生命周期的执行。

在ActivityThread里面对生命周期管理主要分为以下几步:

  1. 拿到AMS跨进程通信传递过来的生命周期事务ClientTranstion,通过sendMessage从子线程发送到主线程的事务处理器处理。
    在主线程的事务处理器里面先执行起始生命周期LaunchActivityItem的exect方法,创建一个ActivityClientRecord,然后把这个对象传递给ActivityThread的handlerlaunchActivity
  2. 在handlerlaunchActivity方法里面通过反射创建出Activity,然后通过makeApplication方法确保此时的Application是被成功创建的,再嘻嘻onCreate方法,并且把此时的生命周期状态ON_CREATE存储在ActivityClientRecord里面
  3. 事务处理器TranstionExect计算起始生命周期状态和结束生命周期状态,发现中间还有一个ON_START的生命周期状态需要执行,于是执行ON_START生命周期
  4. 最后执行ResumeActivityItem的exect方法,调入到handlerResumeActivity里面,执行完activityB的onResume方法,然后把DecorView添加到Window上面
  5. 在handlerResumeActivity尾部,通过一个IdleHandler,在messagequeue没有message执行或者休眠的时候通过binder和AMS通信,调回到ActivityStackSupervior,找到ActivityA对应的ActivityRecord,在ActivityRecord把onStop方法封装成一个事务ClientTranstion,在通过AMS和App进程跨进程通信,把事务传送给App进程,最后通过事务处理器TranstionExect执行ActivityA的相关生命周期

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

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

相关文章

关于爬取所有哔哩哔哩、任意图片、所有音乐、的python脚本语言-Edge浏览器插件 全是干货!

这些都是现成的并且实时更新的!从次解放双手! 首先有自己的edge浏览器基本上都有并且找到插件选项 1.哔哩哔哩视频下载助手(爬取哔哩哔哩视频) bilibili哔哩哔哩视频下载助手 - Microsoft Edge Addons 下面是效果: 2.图…

centOS linux 宝塔 部署django 遇坑小记

按照网上的视频教程,一步一步操作,最后下来还是没有启动成功。 于是通过Xshell 7 登录服务器,进入项目目录,激活虚拟环境,运行 python manage.py runserver 发现报错。 source bin/activate # 激活虚拟环境py…

新人进入互联网行业

今天谈一个很多人关心的问题: 新人如何进入互联网行业? 大部分人都是通过招聘,进入互联网公司。如果你专业对口,是软件类或者美术类的学生,那很简单,专注自己的方向就 OK,行业非常缺人&#xf…

2024年数学建模美赛 A~E 题目解析

2024美赛A题:资源可用性和性别比例 背景 尽管一些动物物种不属于通常的雄性或雌性,大多数物种在出生时要么显著地为雄性,要么为雌性。虽然许多物种在出生时表现出1:1的性别比,但其他物种则偏离了这个均衡的性别比例。这被称为性…

PostgreSQL 也很强大,为何在中国大陆,MySQL 成为主流,PostgreSQL 屈居二线呢?

问题: PostgreSQL 也很强大,为何在中国大陆,MySQL 成为主流,PostgreSQL 屈居二线呢?PostgreSQL 能否替代 MySQL? 当我们讨论为何 MySQL 在中国大陆成为主流而 PostgreSQL 屈居二线时, 我们其实…

Centos7安装Nginx-1.21

一、编译前提,需要安装必要的包 yum install gcc pcre-devel openssl-devel zlib-devel wget -y 二、下载对应的NGINX包 wget http://nginx.org/download/nginx-1.21.0.tar.gz 三、解压nginx tar xf nginx-1.21.0.tar.gz 四、编译并安装nginx到/usr/local/ng…

高中数学立体几何练习题3

用到的基础知识: 1. 2.

C语言中大小写宁母的转化详细讲解

在 C 语言中,可以使用一些函数来实现大小写字母的转换。 toupper() 和 tolower() 函数 这两个函数可以将一个字符转换为大写或小写字母。它们的原型定义在 ctype.h 头文件中: int toupper(int c); int tolower(int c);这两个函数都接受一个字符参数 c…

软件测试过程中出现随机性缺陷,大家是如何处理的?

软件测试是确保软件产品的质量、功能和可靠性的关键过程。然而,测试并不总是完美无缺的,在开发和测试阶段可能会出现缺陷。缺陷是错误、缺陷或偏离软件的预期行为或要求。在本文中,我们将探讨软件测试中缺陷的常见类型和原因,以及…

netstat是一个常用的网络工具,用于显示和分析网络连接、路由表以及网络接口等信息。

netstat 是一个常用的网络工具,用于显示和分析网络连接、路由表以及网络接口等信息。 它可以提供关于网络活动的实时统计数据,包括正在监听的端口、已建立的连接、网络接口的状态等。 使用 netstat 命令可以列出当前系统中的网络连接情况。以下是一些常…

AutoCAD .NET 层次结构介绍

AutoCAD .NET API 提供了一种面向对象的编程接口,通过它可以与AutoCAD进行深度集成和自定义功能开发。以下是基于.NET框架下AutoCAD对象层次结构的基本介绍: Autodesk.AutoCAD.ApplicationServices 命名空间 根对象,代表运行中的AutoCAD应用程…

centos 7 部署若依前后端分离项目

目录 一、新建数据库 二、修改需求配置 1.修改数据库连接 2.修改Redis连接信息 3.文件路径 4.日志存储路径调整 三、编译后端项目 四、编译前端项目 1.上传项目 2.安装依赖 3.构建生产环境 五、项目部署 1.创建目录 2.后端文件上传 3. 前端文件上传 六、服务启…

一文彻底搞懂Java中ArrayList和LinkedList 区别

文章目录 1、数据结构2、插入和删除3、随机访问4、内存占用5、适用场景6、总结7、选择建议8、关于 ArrayList 和 LinkedList 的常见问题 1、数据结构 ArrayList:基于数组实现,元素在内存中是连续存储的。LinkedList:基于链表实现&#xff0c…

第二证券:苹果Vision Pro出货在即 固态电池产业化前景渐明

sion Pro即将于2月2日正式在美国商场开始交给,苹果美国官网释出了Vision Pro的详细参数,与发布会介绍根本一致,依靠总计12个摄像头、5种传感器、职业顶尖的单眼4K分辨率Micro-OLED显示屏、M2与R1芯片,完成了当时商场上独一无二的沉…

有关使用Lombok@Builder注解构建对象返回为空

目录 问题 原因 解决方案 问题 使用lombokBuilder注解构建对象返回结果为空 原因 Builder注解底层调用setter等方法赋值,你如果没加Data注解就赋值不上,idea也不会错误警示,运行也不会抛出异常,这个平时不注意漏掉这个点还真…

SpanDB: A Fast, Cost-Effective LSM-tree Based KV Store on Hybrid Storage——论文泛读

FAST 2021 Paper 论文阅读笔记整理 问题 键值(KV)存储支持许多关键的应用和服务。它们在内存中执行快速处理,但通常受到I/O性能的限制。最近出现的高速NVMe SSD推动了新KV系统设计,以利用其低延迟和高带宽。 挑战 当前基于LSM…

gorm+mysql查询/修改json列相关操作汇总

目录 具体操作 1,查询JSON段落指定key的值是否有等于value的 或 指定keyvalue的数据记录 2,查询JSON段落中price>19的记录 3,查询JSON段中key为k0的记录 4、JSON段落中提取指定键值对到指定结构 5,查询JSON数组是否包含…

ANN(MLP) 三种预测

目录 介绍: 一、Mlp for binary classification 数据: 模型: 预测: 二、Mlp for Multiclass Classification 数据: 模型: 预测: 三、MLP for Regression 数据: 模型&a…

中科大计网学习记录笔记(五):协议层次和服务模型

前言: 学习视频:中科大郑烇、杨坚全套《计算机网络(自顶向下方法 第7版,James F.Kurose,Keith W.Ross)》课程 该视频是B站非常著名的计网学习视频,但相信很多朋友和我一样在听完前面的部分发现信…

《最新出炉》系列入门篇-Python+Playwright自动化测试-10-标签页操作(tab)

1.简介 标签操作其实也是基于浏览器上下文(BrowserContext)进行操作的,而且宏哥在之前的BrowserContext也有提到过,但是有的童鞋或者小伙伴还是不清楚怎么操作,或者思路有点模糊,因此今天单独来对其进行讲…