startActivity启动流程的源码学习

2024-05-09 10:58

本文主要是介绍startActivity启动流程的源码学习,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

举例,当进程A调用startActivity方法,启动进程B,并打开B的Activity,这个过程是怎样的?以下是学习笔记,基于Android 9.0,在线源码查看:https://www.androidos.net.cn/android/9.0.0_r8/xref

  1. 进程A调用startActivity方法,本质上是通过binder通信,调用IActivityManager#startActivity方法
    • 如果该调用在Activity中,方法调用过程:
      ContextImpl#startActivity -> mInstrumentation#execStartActivity -> IActivityManager#startActivity
      备注:IActivityManager对象保存在进程A的单例IActivityManagerSingleton中,进程启动时查询ServiceManager#getService获得,具有缓存作用
       
  2. SystemServer进程的binder线程响应此请求,ActivityManagerService#startActivity方法被调用,过程如下(备注:过程较复杂,涉及Activity,ActivityStack等一连串操作):
    • ActivityManagerService#startActivity
    • ActivityStarter#startActivityMayWait,其中会调用PMS#resolveIntent方法,解析Intent获得ResolveInfo,并创建ActivityInfo等对象
      ResolveInfo rInfo = mSupervisor.resolveIntent((intent, resolvedType, userId, xxx);
    • ActivityStarter#startActivity,其中会创建即将打开的ActivityRecord等
      ActivityRecord r = new ActivityRecord(mService, callerApp, callingPid, callingUid, xxxx);
    • ActivityStack#resumeTopActivityUncheckedLocked
    • ActivityStackSupervisor#startSpecificActivityLocked,这里很重要,会根据目标进程名,查询进程是否已存在,如果存在,将执行realStartActivityLocked方法;如果不存在,将执行startProcessLocked,冷启动目标进程
    void startSpecificActivityLocked(ActivityRecord r, boolean andResume, boolean checkConfig) {// Is this activity's application already running?ProcessRecord app = mService.getProcessRecordLocked(r.processName,r.info.applicationInfo.uid, true);......if (app != null && app.thread != null) {.......realStartActivityLocked(r, app, andResume, checkConfig);return;}......mService.startProcessLocked(r.processName, r.info.applicationInfo, true, 0,
    
    • 假设这里要启动新进程,执行
      ActivityManagerService#startProcessLocked -> ActivityManagerService#startProcess -> Process#start -> ZygoteProcess#startViaZygote
      a. 利用Socket通信通知Zygote进程,孵化出进程B(判断是否启动成功:Socket返回结果中目标进程pid < 0,则启动失败,抛出ZygoteStartFailedEx异常)
      b. 进程B启动成功后,方法继续执行到ActivityManagerService#handleProcessStartedLocked,其中pid等信息填入事先创建好的ProcessRecord对象中,其对应每一个App进程,保存在数组mPidsSelfLocked中

      mPidsSelfLocked是AMS的成员变量,类型:SparseArray<E>,本质是利用2个数组存储,key和object各一个。此处pid为key,ProcessRecord对象为object。查询方法为二分法
       

  3. 目标进程B启动后,入口函数是ActivityThread#main(静态方法),过程如下:
    • 初始化环境,创建ActivityThread对象,其子对象中如:
      a. mH,类型:ActivityThread#H,作用:主线程的Handler,处理如BIND_APPLICATION,CREATE_SERVICE等几十个消息类型,消息来源是mAppThread
      b. mAppThread,类型:ActivityThread#ApplicationThread,作用:binder实体对象,用于接收AMS的binder调用,并将消息转发mH处理
      c. mInstrumentation,类型:Instrumentation,很重要的基础类,官方描述:用来监控系统与应用的所有交互,例如newApplication,callApplicationOnCreate,callActivityOnCreate等都是其子方法
      注:平时解bug时发现,一些App的插件化hook框架,会动态替换mInstrumentation对象,如达到启动Activity时替换目标页面等目的
    • 调用ActivityThread#attach方法 [重要],其中调用IActivityManager#attachApplication,与AMS进行binder通信,将进程的mAppThread传给AMS,类似进行注册
      mAppThread,类型:ApplicationThread extends IApplicationThread.Stub,本质为binder对象,作为服务端,将来接收AMS的binder调用
    • main方法最后调用Looper.loop,进程B的主线程进入循环处理消息模式

 

    public static void main(String[] args) {// 3.1(Binder对象,继承自IApplicationThread.Stub)Environment.initForCurrentUser();Looper.prepareMainLooper();// 3.2ActivityThread thread = new ActivityThread();thread.attach(false, startSeq);if (sMainThreadHandler == null) {sMainThreadHandler = thread.getHandler();}// 3.3Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

 

  1. SystemServer进程,作为服务端,接收到进程B的attachApplication方法调用,过程如下:
    • 从前面提到的mPidsSelfLocked中提取app对象,类型:ProcessRecord,存入applicationThread等信息
    • 注册死亡回调,以便App进程挂掉后,AMS能及时得到通知,并清除App的相关信息
    • 发起binder通信,利用applicationThread对象,调用进程B的ApplicationThread#bindApplication方法,经消息转发,最终由进程B主线程的mH对象处理,消息类型:BIND_APPLICATION
    • 检查是否有页面要显示
      ActivityStackSupervisor#attachApplicationLocked ->ActivityStackSupervisor#realStartActivityLocked(如果进程B有位于栈顶的Activity需显示) -> ClientTransaction#schedule
      利用applicationThread对象,调用进程B的ApplicationThread#scheduleTransaction方法,经消息转发,最终由进程B主线程的mH对象处理,操控Activity生命周期等,消息类型:EXECUTE_TRANSACTION
    • 检查是否有服务要启动
      ActiveServices#attachApplicationLocked -> ActivityStackSupervisor#realStartServiceLocked
      利用applicationThread对象,调用进程B的ApplicationThread#scheduleCreateService方法,后续同上类似
    • 检查是否有广播消息要处理,不再详细描述
    • 检查是否有BackupAgent要创建,不再详细描述

 

    private final boolean attachApplicationLocked(IApplicationThread thread,int pid, int callingUid, long startSeq) {// 4.1ProcessRecord app;if (pid != MY_PID && pid >= 0) {app = mPidsSelfLocked.get(pid);// 4.2try {AppDeathRecipient adr = new AppDeathRecipient(app, pid, thread);thread.asBinder().linkToDeath(adr, 0);app.deathRecipient = adr;} catch (RemoteException e) {// ...}// 4.3thread.bindApplication(processName, appInfo, providers, xxx);// 4.4mStackSupervisor.attachApplicationLocked(app)) {didSomething = true;}// 4.5didSomething |= mServices.attachApplicationLocked(app, processName);// 4.6didSomething |= sendPendingBroadcastsLocked(app);// 4.7thread.scheduleCreateBackupAgent(mBackupTarget.appInfo, xxx);return true;}

 

总结:

至此经前4步,至少5次关键的跨进程通信,startActivity过程已基本完成。简单描述:

  • 进程A调用IActivityManager#startActivity方法,发起binder通信
  • SystemServer进程作为服务端,AMS会检查进程B是否存在,如无,发起Socket通信,通知Zgyote进程孵化出进程B
  • 进程B启动后,调用IActivityManager#attachApplication方法,发起binder通信
  • SystemServer进程作为服务端,AMS会保存进程B的相关信息,发起binder通信,调用进程B的bindApplication,scheduleTransaction等方法
  • 进程B依次完成Application#onCreate等初始化,并执行scheduleTransaction,初始化要显示的Activity,过程结束

 

这篇关于startActivity启动流程的源码学习的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/973216

相关文章

使用MongoDB进行数据存储的操作流程

《使用MongoDB进行数据存储的操作流程》在现代应用开发中,数据存储是一个至关重要的部分,随着数据量的增大和复杂性的增加,传统的关系型数据库有时难以应对高并发和大数据量的处理需求,MongoDB作为... 目录什么是MongoDB?MongoDB的优势使用MongoDB进行数据存储1. 安装MongoDB

SpringBoot项目启动后自动加载系统配置的多种实现方式

《SpringBoot项目启动后自动加载系统配置的多种实现方式》:本文主要介绍SpringBoot项目启动后自动加载系统配置的多种实现方式,并通过代码示例讲解的非常详细,对大家的学习或工作有一定的... 目录1. 使用 CommandLineRunner实现方式:2. 使用 ApplicationRunne

Python实现NLP的完整流程介绍

《Python实现NLP的完整流程介绍》这篇文章主要为大家详细介绍了Python实现NLP的完整流程,文中的示例代码讲解详细,具有一定的借鉴价值,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1. 编程安装和导入必要的库2. 文本数据准备3. 文本预处理3.1 小写化3.2 分词(Tokenizatio

Java汇编源码如何查看环境搭建

《Java汇编源码如何查看环境搭建》:本文主要介绍如何在IntelliJIDEA开发环境中搭建字节码和汇编环境,以便更好地进行代码调优和JVM学习,首先,介绍了如何配置IntelliJIDEA以方... 目录一、简介二、在IDEA开发环境中搭建汇编环境2.1 在IDEA中搭建字节码查看环境2.1.1 搭建步

SpringBoot使用minio进行文件管理的流程步骤

《SpringBoot使用minio进行文件管理的流程步骤》MinIO是一个高性能的对象存储系统,兼容AmazonS3API,该软件设计用于处理非结构化数据,如图片、视频、日志文件以及备份数据等,本文... 目录一、拉取minio镜像二、创建配置文件和上传文件的目录三、启动容器四、浏览器登录 minio五、

bat脚本启动git bash窗口,并执行命令方式

《bat脚本启动gitbash窗口,并执行命令方式》本文介绍了如何在Windows服务器上使用cmd启动jar包时出现乱码的问题,并提供了解决方法——使用GitBash窗口启动并设置编码,通过编写s... 目录一、简介二、使用说明2.1 start.BAT脚本2.2 参数说明2.3 效果总结一、简介某些情

Nginx、Tomcat等项目部署问题以及解决流程

《Nginx、Tomcat等项目部署问题以及解决流程》本文总结了项目部署中常见的four类问题及其解决方法:Nginx未按预期显示结果、端口未开启、日志分析的重要性以及开发环境与生产环境运行结果不一致... 目录前言1. Nginx部署后未按预期显示结果1.1 查看Nginx的启动情况1.2 解决启动失败的

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

Security OAuth2 单点登录流程

单点登录(英语:Single sign-on,缩写为 SSO),又译为单一签入,一种对于许多相互关连,但是又是各自独立的软件系统,提供访问控制的属性。当拥有这项属性时,当用户登录时,就可以获取所有系统的访问权限,不用对每个单一系统都逐一登录。这项功能通常是以轻型目录访问协议(LDAP)来实现,在服务器上会将用户信息存储到LDAP数据库中。相同的,单一注销(single sign-off)就是指