Activity 启动之栈管理与 LaunchMode 判定

当 Activity 的启动请求进入 ATMS 后,最核心的逻辑就是:这个 Activity 应该放在哪个任务栈(Task)里? 这涉及到对 LaunchMode、Intent Flags 以及 TaskAffinity 的综合判定。

1. 核心角色:ActivityStarter

ActivityStarter 是启动流程中的“决策者”。它负责解析启动参数,并调用 startActivityUnchecked 开始处理栈逻辑。

2. LaunchMode 与 栈分配逻辑表

启动模式 ATMS 的核心逻辑 典型表现
standard 不做检查,直接创建新实例。 每次启动都会压入当前栈顶。
singleTop 检查目标栈顶。若已是该实例,回调 onNewIntent 防止栈顶重复创建。
singleTask 在全局寻找是否存在对应 taskAffinity 的栈。若有则移至前台并清理其上方所有 Activity (Clear Top)。 确保一个任务栈中只有一个实例。
singleInstance 强制创建一个新的任务栈,且该栈只能有这一个实例。 独占一个任务栈(如电话界面)。

3. Intent Flags 的影响

代码中通过 Intent.setFlags() 设置的参数优先级高于 XML 定义。

  • FLAG_ACTIVITY_NEW_TASK:对应 singleTask 的基本行为,寻找或创建新栈。
  • FLAG_ACTIVITY_CLEAR_TOP:如果栈中已存在该实例,销毁其上的所有 Activity。
  • FLAG_ACTIVITY_REORDER_TO_FRONT:将已存在的实例移到栈顶,不销毁中间的 Activity。

4. 栈管理的关键步骤

  1. findTask:根据 taskAffinityFlags 寻找匹配的 TaskRecord
  2. computeStack:确定目标 Activity 应该落在哪个 ActivityStack(在 Android 10+ 主要是 DisplayContent 下的容器)。
  3. resumeFocusedStacksTopActivities:将目标栈移到最前台,准备执行显示逻辑。

5. 调试技巧

使用以下命令可以实时观察栈的变化,这是理解栈管理逻辑的“神技”:

1
adb shell dumpsys activity activities

关注点: 寻找 TaskRecord 节点,查看其内部的 ActivityRecord 列表及其 intent 信息。

Activity 启动之进程创建流程(AMS 与 Zygote 的握手)

在启动 Activity 时,如果 ATMS 发现目标进程(ProcessRecord)不存在,就会触发进程创建流程。这是 Android 系统中典型的“按需启动”机制。

1. 触发点:AMS.startProcessLocked

当 ATMS 判定需要新进程来承载 Activity 时,会跨模块回调 AMS:

1
2
3
4
5
// 核心逻辑简述
if (app == null) {
// 进程不存在,开始启动进程
mService.startProcessLocked(processName, info, ...);
}

2. AMS 与 Zygote 的通信流程表

阶段 核心方法 动作描述
1. 决策 startProcessLocked AMS 确定进程参数(UID, GID, 启动类 ActivityThread)。
2. 发起请求 Process.start 通过 ZygoteProcess 建立与 Zygote 的 Socket 连接。
3. 通信 ZygoteState.connect /dev/socket/zygote 写入启动参数字符串。
4. 孵化 Zygote.fork() Zygote 接收信号,Fork 自身进程(利用 COW 机制)。
5. 诞生 RuntimeInit 子进程初始化运行环境,并抛出 MethodAndArgsCaller 异常以进入 Java 入口。

3. 为什么是 Socket 而不是 Binder?

这是一个经典考点。Zygote 使用 LocalSocket 监听请求而非 Binder,主要是为了规避 Fork 机制与多线程锁冲突 导致的死锁风险。在 fork 瞬间,子进程只会拷贝发起调用的线程,其他线程持有的锁将永远无法在子进程中释放。


4. 子进程的“觉醒”:ActivityThread.main()

fork 成功后,子进程会执行 ActivityThread.main() 方法,这标志着应用主线程正式开始工作:

  1. **Looper.prepareMainLooper()**:初始化主线程消息循环。
  2. attach:跨进程向 AMS 报告:“我启动好了(attachApplication)”。
  3. BIND_APPLICATION:AMS 发回应用配置信息,App 开始创建 Application 实例并调用 onCreate
  4. **Looper.loop()**:主线程进入死循环,开始处理 AMS 发来的生命周期指令(如 LAUNCH_ACTIVITY)。

5. 总结

进程创建是一个“从 C++ 到 Java”的过程,也是“从系统服务到应用代码”的过程。掌握了从 startProcessLockedattachApplication 的闭环,就掌握了 Android 进程管理的精髓。

Android Framework 专项 - WMS 学习路线指南

WindowManagerService (WMS) 是 Android 系统中负责窗口管理的核心服务。与 AMS 相比,WMS 与图形显示、输入系统及 View 系统的交互更为紧密。设计一套科学的 WMS 学习路线,建议遵循“从基础支撑到核心模型,再到显示原理”的原则。


第一阶段:支撑系统(底层依赖知识)

在深入 WMS 源码前,必须掌握 WMS 运行的基础设施。

模块 关键考点 学习目的
SurfaceFlinger 图形缓冲区、Layer、合成原理 理解 WMS 最终的渲染产物是如何被合成并显示的。
Input 系统 IMS、事件分发、InputChannel 理解 WMS 如何协助将点击事件派发到正确的窗口。
View 系统 ViewRootImpl、DecorView、Measure/Layout 理解应用端是如何通过 View 系统与 WMS 建立联系的。

第二阶段:核心模型(窗口的骨架)

WMS 服务端通过一组对象来维护窗口的层级、属性和状态。

核心类 职责描述 对应现实模型
WindowToken 标识一组窗口(如 Activity 下的所有 Window),用于权限和组织。 分组标签
WindowState 服务端对单个窗口的建模,存储大小、位置、可见性等信息。 窗口本体
DisplayContent 对应物理或虚拟屏幕,管理该屏幕下的所有窗口。 整个屏幕
Session 应用进程与 WMS 之间的 Binder 通道,每个进程一个。 通信信道

第三阶段:核心流程(窗口的诞生与消亡)

通过追踪一个 Activity 窗口从无到有的过程,理清 WMS 的运行逻辑。

学习步骤 核心关键点 关注重点
1. 窗口添加 (Add) WindowManagerGlobal -> WMS.addWindow 权限校验、WindowState 创建、Z-Order 分配。
2. 布局计算 (Relayout) WMS.relayoutWindow 窗口框架计算(Frame)、显示区域(Insets)的处理。
3. Surface 分配 WindowSurfaceController 理解 Surface 如何从 WMS 传递到应用进程。
4. 窗口移除 (Remove) removeWindow 状态同步、资源清理、窗口退出动画。

第四阶段:进阶专题(高级显示控制)

了解现代 Android 在复杂显示场景下的处理机制。

进阶专题 核心内容 学习意义
层级管理 (Z-Order) BaseLayer、SubLayer 计算规则。 理解窗口遮挡关系与叠加顺序。
窗口动画 WindowAnimationSpec、Remote Animation。 掌握 App 切换动画及系统动画的底层实现。
多窗口与分屏 TaskStack 组织、多任务焦点管理。 适配大屏、折叠屏等现代设备。
输入法窗口 (IME) IME 窗口的特殊层级与生命周期。 解决输入法遮挡及交互的疑难杂症。

💡 学习建议:

  1. 结合 Winscope 工具:利用 Android 提供的 Winscope 录制并查看窗口层级和 Surface 状态,将抽象代码可视化。
  2. 理解 Insets 机制:现代 Android 窗口管理很大一部分精力在于处理状态栏、导航栏、挖孔屏等带来的 WindowInsets
  3. 分层阅读:WMS 源码逻辑分散在 services 的核心包中,建议从 WindowManagerService.java 入手,先理清 addWindow 这一条主线。

WMS 专项 - 窗口添加流程解析 (AddWindow)

当应用端调用 WindowManager.addView 时,窗口的添加流程正式开启。这是一个跨越应用进程与 WMS 服务端的重要 IPC 过程。

1. 应用端:ViewRootImpl 的建立

WindowManagerGlobal 中,每添加一个窗口都会创建一个对应的 ViewRootImpl。它负责:

  • 触发第一次 requestLayout
  • 通过 mWindowSession.addToDisplay 向 WMS 发起跨进程调用。

2. 服务端:WMS.addWindow 核心逻辑

关键步骤 内部行为 目的
1. 权限与令牌校验 检查 WindowToken 是否合法。 确保如子窗口、Activity 窗口拥有正确的宿主权限。
2. 创建 WindowState 在 WMS 内部为该窗口建立“档案”。 存储窗口的所有几何属性、标志位及父子关系。
3. Z-Order 计算 根据 WindowType 分配显示层级。 决定窗口在垂直方向上谁盖住谁。
4. 关联 Session 将窗口与发起进程的 Session 绑定。 便于后续的 IPC 通信与异常清理。

3. 核心对象关系图

App 进程 (ViewRootImpl) <— IWindowSession (Binder) —> SystemServer (WMS)

  • WMS 持有 WindowToken (管理组)
  • WindowToken 包含多个 WindowState (管理个体)

WMS 核心概念与窗口管理流程

WindowManagerService (WMS) 是 Android 系统中负责窗口 management 的核心服务。它主要处理窗口的布局、显示次序、窗口动画以及输入事件的派发。

1. WMS 的职责

WMS 的主要工作可以概括为以下四个核心领域:

职责领域 核心功能描述
窗口管理 控制窗口的添加、删除、更新,并维护窗口的层级(Z-Order)关系。
窗口布局 根据窗口属性(LayoutParams)动态计算每个窗口在屏幕上的大小和位置。
输入事件中转 作为输入系统的中间站,协助 InputManagerService 将触控等事件派发给正确的窗口。
动画与渲染 管理窗口切换动画,并与 SurfaceFlinger 协作完成最终屏幕内容的渲染绘制。

2. 核心类解析

理解 WMS 的源码,必须先搞清楚这几个核心类及其职责:

核心类 职责描述
Window (PhoneWindow) 屏幕窗口的抽象,承载了 ViewTree 及 DecorView。
WindowManager 应用端操作窗口的入口,负责与 WMS 通信。
WMS 服务端核心,负责全局窗口调度、布局、动画及输入分发。
Session 应用进程与 WMS 通信的单向 Binder 通道。
WindowState WMS 服务端窗口实体,存储了窗口的几何属性及状态信息。
WindowToken 窗口令牌,用于标识和组织一组相关的窗口(如 Activity 的所有窗口)。

3. 窗口的层级管理 (Z-Order)

WMS 使用 WindowType 来区分窗口的层级。层级越高,显示在越上层:

窗口类型 层级范围 (Z-Order) 典型示例 备注
应用窗口 1 - 99 Activity 窗口 基础窗口类型
子窗口 1000 - 1999 PopupWindow, Dialog 必须依附于父窗口
系统窗口 2000 - 2999 StatusBar, Toast, Alert 通常需要特定系统权限

4. 窗口添加流程简述

当我们在 Activity 中调用 setContentView 后,窗口诞生的核心步骤如下:

关键步骤 执行角色 核心行为
创建核心纽带 ViewRootImpl 创建实例,作为连接 WindowManager 与 DecorView 的纽带。
发起布局请求 应用进程 调用 requestLayout 触发视图树的测量、布局与绘制流程。
跨进程添加 WMS 通过 IWindowSession 发起 IPC,在服务端创建 WindowState 并分配显示层级。

5. 接下来我们将深入探讨

  • WMS 的启动流程
  • Surface 的创建与渲染链路
  • WMS 动画框架解析

WMS 专项 - 窗口布局与 Relayout 机制

窗口添加后,其大小和位置并非一成不变。每当窗口属性变化、输入法弹出或屏幕旋转时,都会触发 relayoutWindow

1. 什么是 Relayout?

relayout 是 WMS 及其客户端(应用进程)最频繁的交互点。它主要完成:

  • 尺寸计算:WMS 根据屏幕大小、Insets(状态栏等占位)计算窗口的最终 Frame。
  • 配置同步:将最新的 Configuration(如横竖屏、语言)同步给应用。
  • Surface 状态变更:控制 Surface 的显示、隐藏或尺寸调整。

2. 核心算法:窗口框架 (Frame) 计算

框架术语 含义描述
parentFrame 父容器(如 Display 或包含它的 Task)给出的参考范围。
displayFrame 窗口在当前屏幕中的可用范围。
contentFrame 扣除状态栏、导航栏等装饰后,真正用于显示内容的范围。
finalFrame 经过 WMS 策略计算后,窗口最终在屏幕上的物理坐标。

3. Insets 机制(现代 Android 重点)

现代 Android 不再简单地减去状态栏高度,而是使用 WindowInsets 机制。WMS 负责计算这些 Insets(如键盘、手势区域)并将其分发给 View 系统,从而实现沉浸式状态栏或避让输入法。

WMS 专项 - Surface 创建与渲染链路

WMS 不仅管理窗口逻辑,还是图形渲染的“中转站”。它协助应用获取绘制所需的画布 —— Surface

1. Surface 的本质

在 Android 中,Surface 对应 SurfaceFlinger 中的一个 Layer。它本质上是一块图形缓冲区队列(BufferQueue)的生产者端。

2. 创建流程:从 WMS 到 SurfaceFlinger

角色 执行动作
应用进程 relayout 请求中设置 SURFACE_CREATING 标志。
WMS 调用 WindowSurfaceController 开始创建流程。
SurfaceControl WMS 与 Native 层的桥梁,向 SurfaceFlinger 申请创建 Layer。
SurfaceFlinger 分配内存缓冲区,并返回句柄。

3. 渲染“三步走”

  1. **申请 (Lock)**:应用通过 lockCanvas() 或 EGL 申请缓冲区。
  2. **绘制 (Draw)**:应用调用 Canvas 或 GPU 绘图指令。
  3. **提交 (Unlock & Post)**:将绘制好的缓冲区入队,通知 SurfaceFlinger 进行合成显示。

WMS 专项 - 底层支撑系统 (SurfaceFlinger, Input, View)

WMS 的运行并非孤立,它深度依赖于图形合成、输入分发和视图系统。理解这些支撑系统,是掌握 WMS 源码逻辑的基石。


1. SurfaceFlinger:WMS 的图形“甲方”

WMS 负责管理窗口的逻辑层级,而 SurfaceFlinger 负责将这些层级真实地合成并渲染到屏幕上。

核心考点 详细解析 学习目的
图形缓冲区 (BufferQueue) Surface 内部维护的生产者-消费者模型队列。 理解应用绘制的数据是如何传递给 SurfaceFlinger 的。
Layer (层) SurfaceFlinger 内部对窗口的映射,每个窗口对应一个 Layer。 理解 WMS 的 WindowState 最终是如何映射为渲染层的。
合成原理 (Composition) VSync 触发,将多个 Layer 按照 Z-Order 合并。 掌握 Android 60/120 fps 流畅度的物理基础。

2. Input 系统:事件分发的“中转站”

WMS 协助 InputManagerService (IMS) 将触摸事件分发到正确的窗口。

核心考点 详细解析 学习目的
IMS 与 Socket 通信 IMS 通过 InputChannel(基于 Unix Domain Socket)与应用通信。 理解为什么输入事件分发不使用 Binder。
窗口定位 WMS 维护全局窗口位置,告知 IMS 哪个坐标对应哪个窗口。 理解点击屏幕后,系统如何“定位”到你的按钮。
事件分发链 IMS -> WMS (定位) -> ViewRootImpl -> DecorView -> ViewGroup -> View。 建立完整的事件分发全景图。

3. View 系统:WMS 的“应用端代理”

应用端通过 View 系统与 WMS 建立联系。

核心考点 详细解析 学习目的
ViewRootImpl 整个 View 树的根节点,负责与 WMS 的 Session 进行 IPC 通信。 理解 WindowManager.addView 背后的第一站。
DecorView 窗口的根视图,包含状态栏和内容区。 明确 Activity 布局的物理边界。
Measure/Layout 触发 relayoutWindow 成功后,WMS 反向驱动应用端执行绘制。 理清“系统驱动”与“应用主动”的逻辑闭环。

4. 总结:三大系统与 WMS 的协作关系

  • WMS + SurfaceFlinger:解决“在哪里画,画多大”的问题。
  • WMS + Input 系统:解决“谁接收点击”的问题。
  • WMS + View 系统:解决“具体画什么”的问题。

掌握了这三者的协作,就掌握了 Android 系统 UI 展示的核心脉络。

Android Framework 专项 - IPC Binder 机制(一)

Binder 是 Android 系统中最重要的进程间通信(IPC)机制。

1. 为什么 Android 选用 Binder?

在 Linux 系统中,已经存在多种 IPC 方式,如管道、信号、消息队列、共享内存、Socket 等。Android 选用 Binder 主要出于以下考虑:

  • 性能(效率):共享内存虽然最快(0 次拷贝),但管理复杂;Socket 效率低(2 次拷贝);Binder 只需要 1 次数据拷贝,性能仅次于共享内存。
  • 安全:传统 IPC 的接收方无法获得发送方可靠的 UID/PID,难以进行权限校验。Binder 在内核层添加了身份校验,安全性更高。
  • 稳定性:Binder 基于 Client-Server 架构,职责明确,容错性好。

2. Binder 的分层架构

Binder 的实现可以分为四个层次:

  1. **内核层 (Binder Driver)**:运行在内核空间,是所有 Binder 通信的枢纽。负责物理内存映射(mmap)和数据的跨进程传递。
  2. **驱动抽象层 (Libbinder)**:在 Native 层对驱动操作进行封装(如 ProcessState, IPCThreadState)。
  3. **框架层 (Java Binder)**:提供给 Java 开发者使用的接口(如 IBinder, Binder, Stub, Proxy)。
  4. 应用层:具体的业务实现(如 AIDL 定义的 Service)。

3. Binder 通信的核心:内存映射 (mmap)

Binder 之所以能实现“一次拷贝”,核心在于 mmap

  • Binder 驱动会在内核空间分配一段缓冲区。
  • 驱动会将这段内核缓冲区同时映射到接收进程的用户空间
  • 发送进程通过 copy_from_user 将数据拷贝到内核缓冲区,由于映射关系存在,接收进程直接就能在自己的用户空间读到这些数据。

4. 总结

Binder 是 Android 系统的灵魂。理解了 Binder,才能真正理解 AMS、WMS 等系统服务是如何与应用进程协同工作的。

Android Framework 专项 - Zygote 进程与进程孵化机制

Zygote(受精卵)进程是 Android 系统中所有应用进程和 SystemServer 进程的“鼻祖”。理解 Zygote 机制是深入掌握 Android 系统启动及应用进程创建的关键。


1. Zygote 的核心职责

Zygote 进程在系统启动时由 init 进程解析 init.rc 脚本启动。它的主要工作可以概括为以下三点:

职责 详细描述
预加载资源 (Preload) 加载系统常用的类库(Framework 类)、资源(Drawables, XML)及共享库,供所有子进程共享。
启动 SystemServer 孵化出 Android 的核心服务进程 SystemServer,由其启动 AMS, WMS 等系统服务。
孵化应用进程 (Fork) 通过 Socket 监听请求,每当需要启动新应用时,通过 fork 机制快速创建子进程。

2. 核心机制:Fork 与写时复制 (COW)

为什么 Android 选择使用 Zygote 来孵化进程,而不是直接启动?

  • 启动速度快:Zygote 已经预加载了大量的共享类和资源。通过 Linux 的 fork() 系统调用创建子进程时,子进程直接继承了 Zygote 的内存状态。
  • **节省内存 (Copy-On-Write)**:
    • fork 后的子进程与父进程(Zygote)共享相同的物理内存页。
    • 只有当子进程尝试修改这些内存页时,内核才会真正为子进程分配新的内存副本。
    • 这种机制极大地减少了系统内存的冗余开销。

3. Zygote 的启动流程分析

关键步骤 执行角色 核心行为
1. 解析脚本 init 进程 解析 init.zygote.rc 文件,执行 app_main.cpp
2. 开启虚拟机 AppRuntime 调用 AndroidRuntime.start(),启动 ART/Dalvik 虚拟机并注册 JNI 方法。
3. 进入 Java 层 ZygoteInit 通过 JNI 调用 ZygoteInit.main(),标志着 Zygote 正式从 C++ 切换到 Java 环境。
4. 开启 Socket 监听 ZygoteServer 建立 /dev/socket/zygote 本地套接字,等待来自 AMS 的进程创建请求。

4. 从 AMS 启动应用进程的调用链

当 AMS 发现目标进程不存在时,会触发以下调用:

  1. AMS 服务端:调用 Process.start() 发起进程创建请求。
  2. 消息传递:通过 ZygoteProcess/dev/socket/zygote 写入启动参数。
  3. Zygote 服务端:监听到 Socket 消息,调用 Zygote.forkAndSpecialize()
  4. 进程诞生
    • 父进程(Zygote):继续循环监听 Socket。
    • 子进程(新 App):初始化 ActivityThread,进入 main 函数,开启主线程 Loop。

5. 总结

关键术语 定义与作用
AppRuntime Zygote 的启动载体,负责虚拟机的创建与 JNI 环境初始化。
Preload 资源预取技术,是 Android 应用能够快速启动的根本原因。
LocalSocket AMS 与 Zygote 通信的唯一方式(而非 Binder,因为 Fork 之后 Binder 通信会产生不可控的状态冲突)。

Zygote 的设计精妙地结合了 Linux 系统的底层特性(Fork/COW)与 Android 资源共享的需求,是 Android 系统稳定运行的基石。