应用启动与多进程 面试知识点

整理自模拟面试,覆盖应用启动流程 / 冷启动热启动 / Zygote / Activity 生命周期与绘制链路 / SurfaceFlinger / 多进程 / SharedPreferences 与 DataStore


1. 从点击桌面图标到页面显示,发生了什么

一句话:Launcher 发起启动请求,系统服务调度目标组件,必要时先创建应用进程,再创建 Application 和首页 Activity,最后完成 View 绘制并由系统合成显示。

主链路

  1. 用户点击桌面 App 图标
  2. Launcher 构造带 MAIN + LAUNCHER 的启动 Intent
  3. 通过 Binder 调用 ActivityTaskManagerService / ActivityManagerService
  4. 系统检查目标应用进程是否已存在
  5. 如果进程不存在,则通知 Zygote fork 新进程
  6. 新进程进入 ActivityThread.main(),初始化主线程 Looper 和消息队列
  7. 系统回调 bindApplication,创建 Application 并执行 Application#onCreate()
  8. 系统调度启动目标 Activity
  9. 应用进程内创建 Activity,依次执行 onCreate -> onStart -> onResume
  10. setContentView 创建 DecorView 和 View 树
  11. ViewRootImpl 发起 measure -> layout -> draw
  12. 绘制结果通过 Surface 提交,由 SurfaceFlinger 合成后显示到屏幕

面试速记


2. 冷启动、温启动、热启动的区别

冷启动

定义:应用进程不存在,需要从创建进程开始走完整启动链路。

典型流程
无进程 -> 创建进程 -> Application 初始化 -> Activity 创建 -> 首屏绘制

特点
- 链路最长
- 耗时最高
- 主要成本在进程创建、应用初始化、首帧渲染

温启动

定义:应用进程还在,但目标页面需要重新创建或恢复。

典型流程
有进程 -> 创建/恢复 Activity -> 重新绘制首屏

特点
- 不需要重新 fork 进程
- 但仍有页面创建和渲染成本

热启动

定义:应用进程和页面大多仍在,只是从后台快速切回前台。

典型流程
有进程/有页面 -> 恢复前台 -> 恢复交互

特点
- 路径最短
- 用户感知最快

面试回答建议


3. Zygote 为什么能提速

一句话Zygote 通过预加载常用类和资源,再使用 fork + 写时复制 创建应用进程,避免每次从零初始化运行时环境。

提速原因

  1. 预加载
  2. 系统启动时,Zygote 会预加载 Java 核心类、Android Framework 类和部分资源
  3. 应用进程创建时不需要完全重新初始化

  4. fork 创建进程

  5. 启动 App 时不是从零拉起全新虚拟机
  6. 而是让 Zygote 直接 fork 子进程

  7. 写时复制

  8. fork 后父子进程最开始共享内存页
  9. 只有真正修改时才复制
  10. 启动成本和内存开销都更低

一句话速记

Zygote 提速的本质是:预加载 + fork + Copy-on-Write


4. Activity 生命周期和绘制流程是怎么串起来的

一句话:生命周期负责组件状态切换,绘制流程负责把界面真正显示出来,两者在 setContentViewViewRootImpl 这一层串联。

串联链路

  1. 系统启动 Activity
  2. 执行 onCreate
  3. onCreate 中调用 setContentView
  4. 创建 DecorView 和 View 树
  5. 执行 onStart
  6. 执行 onResume
  7. ViewRootImpl 发起 performTraversals
  8. 依次执行 measure -> layout -> draw
  9. 绘制结果提交给底层渲染系统
  10. 用户看到首帧

关键理解

面试补充


5. SurfaceFlinger 是什么

一句话SurfaceFlinger 是 Android 图形系统中的系统合成器,负责把各个窗口和应用提交的图层合成为最终屏幕画面。

它的职责

  1. 管理系统中可见窗口对应的 Surface
  2. 按层级、透明度、裁剪区域等规则合成多个图层
  3. 配合显示系统把最终结果输出到屏幕

在渲染链路中的位置

App 执行 draw
-> 绘制到自己的 Surface Buffer
-> Buffer 交给 SurfaceFlinger
-> SurfaceFlinger 合成所有窗口图层
-> 显示到屏幕

面试常见对比

一句话速记

View.draw() 不是直接画到屏幕,而是先画到缓冲区;真正把内容合成上屏的是 SurfaceFlinger


6. 如果应用是多进程的,启动流程有什么区别

一句话:系统会优先拉起“被启动组件所在的进程”,而不是固定先起主进程;每个进程都有自己独立的运行环境和一份 Application 生命周期。

关键区别

  1. 先启动哪个进程,取决于目标组件声明在哪个进程
  2. 如果 Launcher Activity 在默认进程,就先拉起主进程
  3. 如果 Launcher Activity 显式声明到 :remote,就先拉起远程进程

  4. 每个进程都独立

  5. 有独立虚拟机
  6. 有独立主线程和消息队列
  7. 有独立内存空间
  8. 单例、静态变量、缓存都不共享

  9. Application#onCreate() 会在每个被拉起的进程中执行一次

  10. 多进程下最容易踩坑
  11. 如果初始化逻辑不分进程,就可能重复初始化 SDK、数据库、埋点

  12. 跨进程能力要走 IPC

  13. 不能直接共享普通对象
  14. 需要 Binder、AIDL、Messenger、ContentProvider 等方式协作

面试速记


7. SharedPreferences 为什么早期多进程一致性差

一句话SharedPreferences 主要基于进程内缓存优化读性能,不是为多进程强一致设计的;多个进程各自持有一份内存副本,就容易出现脏读和覆盖写。

根因

  1. 读取时通常先把 XML 文件解析到内存 Map
  2. 后续大量读取直接走当前进程内存缓存
  3. 多个进程各有各的缓存副本,不会天然实时同步

常见问题

关于 MODE_MULTI_PROCESS

工程建议


8. Jetpack DataStore 有什么优点

一句话DataStore 是 Jetpack 对轻量本地配置存储的现代化升级,提供了更自然的异步读写、一致性保障和更好的类型安全能力。

核心优点

  1. 异步读写
  2. 基于协程和 Flow
  3. 更符合现代 Android 架构
  4. 减少主线程阻塞风险

  5. 一致性更好

  6. 更新操作是串行处理的
  7. 单次 edit / updateData 的原子性更清晰

  8. 天然响应式

  9. 直接暴露 Flow
  10. 很适合与 ViewModelStateFlow、Compose 配合

  11. 类型安全更好

  12. Proto DataStore 可通过 protobuf 定义结构化字段
  13. 比字符串 key 更清晰、更稳

  14. 错误处理和迁移能力更明确

  15. 能更清楚地处理文件损坏、反序列化失败
  16. 支持从 SharedPreferences 平滑迁移

trade-off


9. DataStore 支持多进程吗

结论:支持,但不能把普通单进程 DataStore 直接当成天然多进程安全;多进程场景要使用官方提供的多进程支持能力。

为什么默认心智还是单进程

多进程场景的正确理解

  1. 单进程版 DataStore 不等于跨进程共享方案
  2. 多进程要显式使用官方多进程支持能力
  3. 高频复杂共享状态,很多时候数据库、Binder、ContentProvider 仍然更合适

面试回答建议


10. 这类问题在面试里的推荐答法

回答顺序

  1. 先给一句话结论
  2. 再按系统调度、进程创建、组件创建、界面渲染四层展开
  3. 如果问到多进程,再补进程隔离、Application 多次初始化和 IPC 成本
  4. 如果追问本地存储,再从一致性和使用边界讲 SharedPreferencesDataStore

推荐表达

一句话速记