一文搞定面试 | Activity启动流程之冷热启动(上)

1,264 阅读7分钟

Activity启动流程由于篇幅较长,将分述两篇文章进行讲解。

上文主要进行概念提要和流程综述,下文主要从源码角度逐步探寻流程经过

一文搞定面试 | Activity启动流程之冷热启动(上)

一文搞定面试 | Activity启动流程之冷热启动(下)

概念提要

什么是冷\热启动

直观来说,区分冷\热启动的外在表现就是点击桌面应用图标时,打开APP所需的时间长短

应用在启动过程中,需要依赖其所属进程和一些相关对象(如Application、Activity)。其中冷启动是应用从无到有的过程,需要创建新的进程和对象,耗时较长;而热启动则是从有到有,仅需要进行对象的状态恢复,因而耗时较短;期间还有另一种场景,部分对象被回收了,但进程仍然存活,则为暖启动,耗时适中,这种场景可通过开发者选项-不保留活动进行模拟

为什么启动应用需要新的进程

dalvik虚拟机(DVM,与JVM有一定的关系,但是不完全相同,它们都可以执行Java字节码,但是DVM使用了不同的指令集和文件格式)是一种为Android应用提供运行环境的虚拟机,它是针对手机硬件资源有限而设计的。每个应用启动都运行一个单独的DVM,每个DVM单独占用一个Linux进程。这样可以保证应用之间的隔离和稳定性。

新进程是如何创建的

Android开机从Linux内核启动角度可以如此描述:

  1. 初始化CPU、内存、中断、时钟等硬件资源,并创建第一个用户进程init
  2. init进程会解析init.rc文件,创建一些用户空间的守护进程和服务,其中第一个创建的Java进程是Zygote进程也是所有Java进程的父进程
  3. Zygote进程会加载虚拟机、预加载类和资源,并注册Zygote Socket服务端套接字,等待AMS请求创建新的应用进程。Zygote进程还会forkSystem Server进程,其中包含AMS、WMS、PMS等重要服务

以上透露了几个重要信息:

  1. System Server进程是由Zygote进程fork出来的,且包含AMS等服务
  2. 应用进程是由AMS向Zygote发起Socket通信进行创建的

由此产生两个问题:

  1. 为什么进程都是fork出来的?

fork会复制一些如堆栈、地址空间、寄存器等资源到子进程,以实现资源共享,节省其重新分配和初始化的所需的时间和内存,达到提效的目的

  1. 通常我们有了解Android的进程间通信采用的是Binder,那AMS请求fork时为何采用Socket?

三点原因:多线程问题、注册时间问题、性能损耗

  1. Binder机制需要创建线程池,fork一个多线程的进程可能导致锁异常
  2. Binder机制源于ServiceManager,且需要注册服务。而Zygote进程是首个Java进程,Socket通信只需要监听端口即可
  3. Binder机制虽然效率更高,但需要进行数据拷贝和序列化等操作,而申请fork进程时传递的数据较简单,不需要复杂的数据处理,所以这种场景下Socket机制可能更节省资源

进程间通信与Binder

进程间的通信是内核空间用户空间的数据交换,copy_from_user和copy_to_user是有性能开销的,而选择Binder,就是由于其使用了mmap(不作展开)实现了数据在内核空间和用户空间的共享,避免了多次拷贝。当然还有其他技术优化了整体的通信效率,这里不作赘述

Binder是一种基于Client-Server模式的进程间通信(IPC)机制 Binder时序图.png

从图上我们可以了解到:

  1. 客户端发起请求使用Proxy、服务端处理请求使用Stub,(这个是另外总结的)而Service Manager是服务管理集中侧,服务端注册服务后得到Binder关联服务名,客户端根据服务名获取对应Binder从而转化为Proxy以调用
  2. 数据在传递过程中需要进行序列化和反序列化
  3. 在Server处理过程中,Client线程处于挂起状态。而Server在不处理时处于休眠状态
  4. mmap主要应用在数据包的复制传递环节

在设计上,采用IBinder接口,上图中的流程抽象为transact()onTransact(),然后根据官方API注释给出如下阐述

这些方法允许你向一个IBinder对象发送一个调用,并接收一个传入到Binder对象的调用。

这个事务API是同步的,这意味着对{@link #transact transact()}的调用在目标从{@link Binder#onTransact Binder.onTransact()}返回之前不会返回;这是在调用本地进程中存在的对象时的预期行为,

底层的进程间通信(IPC)机制确保了当跨进程时也具有相同的语义。

流程综述

后续关于启动流程均以冷启动为例,热启动包含其中,不作单独叙述,仅进行简单提点带过

已知,桌面Launcher是一个应用

先过冷启动新建应用进程的流程

  1. 首先,在桌面当点击应用图标时,会先判断应用对应进程是否存在,如果不存在的话,就需要创建新进程
  2. 而应用进程的管理在大名鼎鼎的AMS中,所属System Server进程,由ServerManager派生
  3. 而应用进程的产生,由Zygote进程进行fork,从AMS进行发起,采用Socket通信
  4. 那Launcher作为一个应用,需要判断应用进程是否存在,自然应该通过Binder询问AMS,如果不存在,再由其请求Zygote发起fork

那应用进程创建后需要做什么呢?

  1. 应用进程创建后,需要进行创建主线程,并绑定给AMS,然后开启looper
  2. 为什么要绑定给AMS,这个等下展开说说,因为AMS需要集中管理全部应用的活动堆栈四大组件
  3. 绑定完成后,AMS则需要通知Application进行startActivity,至此完成启动

AMS是如何集中管理全部应用的?

AMS是Android系统的核心服务之一,它负责四大组件(Activity、Service、BroadcastReceiver、ContentProvider)的启动、调度和管理,以及进程的创建、销毁和优先级调整等工作。

这张图非常直观,来看看其中的几个要素:

  1. ActivityRecord:即对应一个Activity
  2. TaskRecord:了解Activity启动模式的,应该知道singleInstance会有单独的任务栈,包括taskAffinity的栈亲和设置,都会影响ActivityRecord归属的TaskRecord任务栈
  3. ActivityStack:则是维护一个应用内的TaskRecord,与TaskRecord结合实现了不同的启动模式和任务栈的切换,满足不同的需求和场景。也可以实现多窗口环境和多任务处理,提高用户体验和效率。
  4. ActivityStackSupervisor:则是AMS进行统一管理Activity堆栈的大管家了,位于top栈顶的ActivityStack,即为被focus的应用
  5. ProcessRecord:顾名思义就是应用进程的包装,前面提到新的应用进程完成创建后需要attach绑定给AMS

image.png

一图看懂

通过对以上内容的理解和掌握,再来结合看这张图,基本对Activity启动流程也就不这么望而生畏了

当然图中一些类并没有提到,因为在整体流程中是上下承接的职责分工作用,并非主体内容,将在一文搞定面试:Activity启动流程之冷热启动(下)中跟随源码逐步探寻和梳理

image.png

图片取自 # Android Application 启动流程分析及其源码调用探究

拜读

第一篇主攻源码流程,第二篇主攻概念及思路解析,两篇的阅读顺序可以不一定,但结合起来,非常有助于理解

# Android应用启动流程

# Android应用启动流程分析

# Android Application 启动流程分析及其源码调用探究

如果觉得本文对你有帮助,不妨 点赞+收藏+关注 支持一波,感谢肯定和青睐

当然,更希望初读点赞再读收藏三而关注