Appearance
Android 架构总览:分层、线程模型、组件系统与 UI 范式
模块位置:并发与运行时底座 README
相关主题:Android 主线程消息循环:Looper / Handler / MessageQueue · Android Binder IPC 机制与跨进程通信 · Android ANR 触发机制、排障定位与防范 · Android 生命周期总览 · ViewModel 架构、SavedStateHandle 与进程被杀恢复 · Jetpack Compose 状态模型、StateFlow 与重组优化 · Android App 进程启动全流程
一句话定义
Android 不是“一个 Java UI SDK”,而是一个由 Linux Kernel / 系统服务 / Native 库 / Android Runtime / Framework API / 应用组件 逐层叠起来的平台:线程、消息循环、Binder、生命周期、View/Compose、WebView、Flutter 宿主都只是这张大图里的不同切面。
为什么需要
如果只按 Activity、Handler、ViewModel、Compose、Flutter Plugin 这些局部知识点工作,平时能写业务,但一旦进入下面这些问题,判断会开始飘:
- Android 整个平台到底是怎样分层的,
app_process、Framework、ART、Native、Kernel 各自负责什么?- 一句话答:Kernel 提供调度/驱动/进程隔离,Native 提供 Binder/图形/媒体等系统能力,ART 承载 Java/Kotlin 代码执行,Framework 把系统服务包装成应用 API,App 层再用组件模型把页面、服务、广播和内容提供者组织起来。
- 线程、消息循环、组件生命周期在这张图里分别属于哪一层?
- 一句话答:线程与消息循环属于“运行时执行机制”,组件生命周期属于“Framework 对应用组件的调度契约”,二者在主线程上交汇成 Activity/Service/BroadcastReceiver 的真实执行现场。
- 为什么 Android 既能跑 Java/Kotlin,又能承载 JNI/NDK、WebView、Flutter、游戏引擎?
- 一句话答:因为 Android 在 App 进程里同时提供了托管运行时(ART)、Native 能力(JNI/NDK + 系统 C/C++ 库)、以及可嵌入的渲染/执行容器(WebView、FlutterEngine、游戏引擎 Surface),上层都统一挂到同一套窗口、输入、生命周期和系统服务上。
- XML View 体系和 Jetpack Compose 到底只是“写法不同”,还是运行时架构已经变了?
- 一句话答:不是只有语法不同;View 是命令式操作 View 树,更新靠
invalidate/requestLayout驱动遍历,Compose 是声明式运行时,靠 snapshot 读写追踪、slot table 和记忆化重组来决定哪些节点重新执行。
- 一句话答:不是只有语法不同;View 是命令式操作 View 树,更新靠
- Flutter 工程师看 Android 架构时,最该把哪几个宿主边界看清?
- 一句话答:最该看清的是“Flutter 不是脱离 Android 运行”,它仍要依附 Android 进程、线程、窗口、输入、Binder 和生命周期;只是 UI 渲染与业务代码多了一层 Flutter engine/runtime。
对目标读者来说,这页真正要解决的不是入门定义,而是三件事:
- 把 Android 现有经验重新收束成一张稳定的体系地图。
- 把 Looper / Binder / 生命周期 / 启动 / ViewModel / Compose / Flutter 宿主这些专题放回统一坐标系。
- 建立“Android 为什么能容纳多技术栈形态”的解释能力,而不是只会背 API。
底层机制
1. 先把 Android 放回一张分层图
这张图不是教材式“从上到下背名词”,而是帮助你判断:代码写在哪层、问题发生在哪层、优化应落在哪层。
可以把它按“谁给谁提供能力”来理解:
| 层级 | 你平时直接接触什么 | 它向上一层提供什么 | 常见问题落点 |
|---|---|---|---|
| 应用层 | Activity、Fragment、Compose、ViewModel、WebView、Flutter 宿主 | 业务页面、状态、交互、插件接入 | 生命周期错位、主线程阻塞、状态丢失 |
| Framework | Context、AMS/WMS/PMS、View System、Looper/Handler、资源系统 | 组件模型、窗口、输入、权限、调度 | 启动/返回栈/ANR/配置变更 |
| Runtime | ART、ClassLoader、GC、JNI | Java/Kotlin 执行环境、托管堆、语言桥接 | 冷启动、GC 抖动、JNI 边界 |
| Native | Binder、SurfaceFlinger、Skia、MediaCodec、SQLite、WebView 内核 | IPC、渲染、媒体、数据库、浏览器内核 | Binder 卡顿、渲染瓶颈、图片/视频链路 |
| Kernel | Binder driver、调度器、内存与设备驱动 | 线程调度、进程隔离、驱动访问 | 系统级阻塞、I/O、驱动、OOM/LMK |
这张图里有三个容易混淆的点:
- Framework 不是 Runtime
- 一句话答:Framework 是 Android 对应用暴露的系统 API 与组件契约;Runtime 是 Java/Kotlin 代码真正被执行、回收、装载的环境。
- Native 不只等于“你写 NDK”
- 一句话答:更大量的 Native 能力其实来自 Android 自己的系统库和系统服务,例如 Binder、Skia、SurfaceFlinger、WebView 内核、媒体栈。
- App 层技术形态可以不同,但最终都要落回同一宿主平台
- 一句话答:无论你写的是 XML View、Compose、Flutter 还是 WebView 页面,最后都要依附同一个 Android 进程、窗口系统、输入通道、生命周期和系统服务。
2. 线程、消息循环、组件模型在整体中的位置
先读细节专题: Android 主线程消息循环:Looper / Handler / MessageQueue · Android ANR 触发机制、排障定位与防范
Android 的“应用执行现场”可以拆成三条线:
- 线程线:代码最终在哪条线程上执行。
- 消息循环线:主线程如何串行处理输入、生命周期、布局、绘制、回调。
- 组件模型线:Framework 何时创建/恢复/暂停/销毁你的 Activity、Service、Receiver、Provider。
把它们放回分层图后,关系会更清楚:
text
Kernel 调度线程
→ Runtime/Framework 在主线程初始化 Looper
→ Framework 通过 MessageQueue 分发生命周期 / 输入 / 绘制 / IPC 回调
→ App 代码在 Activity / Fragment / View / Compose / Service 中执行更具体一点:
- 线程:底层由 Linux Kernel 调度,ART/Framework 只是借这些线程跑 Java/Kotlin 代码。
- Looper / MessageQueue / Handler:属于 Framework 侧对主线程执行模型的封装,是 Android 把“单线程 UI + 消息驱动”落地的关键。
- 组件生命周期:由 AMS、ActivityThread、Instrumentation、FragmentManager 等 Framework 机制调度,本质上还是通过主线程消息回调进入你的代码。
所以,下面这些看似不同的问题,其实都在同一个交点上:
| 问题 | 本质上卡在哪 | 典型专题 |
|---|---|---|
| 点击没反应 / ANR | 主线程消息队列被堵 | Android ANR 触发机制、排障定位与防范 |
runOnUiThread / Dispatchers.Main 为什么有效 | 往主线程 Looper 投递恢复任务 | Android 主线程消息循环:Looper / Handler / MessageQueue |
| 页面返回后又重复请求 | 生命周期回调边界用错 | Android 生命周期总览 |
| ViewModel 旋转后还活着 | 组件重建与状态容器边界不同 | ViewModel 架构、SavedStateHandle 与进程被杀恢复 |
| 首帧慢 / 掉帧 | 主线程与渲染管线的同步点失控 | Android App 进程启动全流程 (Zygote → ActivityThread → 首帧) |
一句话收束:
- 线程回答“谁在跑”。
- Looper/MessageQueue回答“主线程按什么顺序跑”。
- 组件模型回答“系统什么时候让你跑哪段代码”。
3. 为什么 Android 能承载 Java/Kotlin + Native + WebView/Flutter 等多种形态
Android 的开放性不是“什么都能塞进去”,而是它在同一个 App 进程内同时提供了三种基础承载能力:
| 能力层 | 你得到的能力 | 典型技术形态 | 为什么能共存 |
|---|---|---|---|
| 托管运行时 | GC、ClassLoader、反射、Kotlin/Java API | Java / Kotlin / Jetpack | 都运行在 ART 上,吃同一套 Framework API |
| Native 桥接 | JNI、NDK、C/C++ 库、Surface、EGL、媒体编解码 | 游戏引擎、音视频、图像、加密、数据库 | 通过 JNI/NativeActivity/Surface 与系统能力打通 |
| 可嵌入容器 | 浏览器内核或跨端引擎 | WebView、FlutterEngine、React Native、小游戏容器 | 都把渲染结果挂到 Android Window/View/Surface 上,并受生命周期与输入系统约束 |
你可以把 Android 想成“宿主操作系统 + 应用运行平台”,它允许多技术栈共存,但要求它们服从几个公共边界:
- 进程与权限边界:都运行在 Android 应用沙箱里。
- 生命周期边界:都要跟随 Activity/Fragment/Application/Process 的可见性与存活性。
- 线程边界:都受主线程响应、渲染线程、后台线程分工影响。
- 系统服务边界:都通过 Binder/Framework API 请求相机、定位、通知、窗口、包管理等能力。
3.1 Java / Kotlin:最“原生”的托管层应用形态
这条线最贴近 Android 官方应用模型:
- UI:XML View 或 Compose
- 业务逻辑:Java/Kotlin
- 状态:ViewModel / Flow / SavedStateHandle
- 系统能力:Framework API + Binder 服务
它的优势是组件模型和平台能力耦合最自然,代价是需要直接面对生命周期、主线程、权限与设备碎片化。
3.2 Native / JNI / NDK:为了性能、复用和系统级能力
Native 被引入通常不是为了“写得更底层更酷”,而是为了几类现实诉求:
- 复用已有 C/C++ 能力:音视频、图像算法、协议栈、数据库、游戏引擎。
- 追求更高性能或更低延迟:编解码、渲染、推理、加密。
- 需要直接触碰系统底层能力:EGL、OpenGL/Vulkan、AImageReader、AAudio 等。
但 Native 并不会绕开 Android 宿主:
- 页面显示仍要进 Window/Surface。
- 权限申请仍走 Framework。
- 生命周期仍由 Activity/Process 管。
- 线程阻塞照样能拖垮主线程或 Binder 线程。
3.3 WebView:把浏览器内核嵌入 Android 组件模型
WebView 之所以强,不是因为它“像网页”,而是因为它把一个浏览器运行时嵌进了 Android 页面:
- Android 负责宿主窗口、生命周期、权限、进程与系统服务。
- WebView 负责 HTML/CSS/JS 执行与页面渲染。
- 原生与 H5 通过 JSBridge / URL Scheme / MessageChannel 之类机制通信。
这意味着 WebView 不是“脱离 Android 的第二世界”,而是“浏览器容器挂在 Android 宿主里”。因此 WebView 的登录态、权限、文件选择、回退栈、性能与崩溃排查,都要同时看 H5 和宿主两边。
3.4 Flutter:自带 UI Runtime,但仍是 Android 宿主上的来宾
Flutter 经常被误解成“和 Android 平级”。更准确的说法是:
- Flutter 自带 Dart runtime 和 engine。
- 但 FlutterEngine 运行在 Android App 进程里。
FlutterActivity/FlutterFragment/FlutterView仍依附 Android 的 Activity、Window、输入、生命周期。- 插件调用仍需要通过 Platform Channel 回到 Android 宿主,最终调用 Binder/Framework/系统服务。
所以 Flutter 在 Android 上的边界判断应该是:
- Dart UI 逻辑 不等于 Android 宿主生命周期。
- Flutter 的 UI isolate / raster / platform 线程 只是多加了一层执行模型,不会取消 Android 主线程、Binder、输入分发、权限和窗口系统的存在。
- Flutter 页面卡顿 仍可能源自 Android 宿主侧的主线程阻塞、平台通道阻塞或系统渲染瓶颈。
对 Flutter 工程师最重要的一句话是:Flutter 替换的是 UI runtime,不是 Android 作为宿主平台的底座。
3.5 Android / Compose / Flutter 宿主关系图
如果你已经熟悉 Android 和 Flutter,这里最值得一眼看清的不是 API 名词,而是宿主层级到底怎么套:Activity/Fragment 决定页面与生命周期边界,Compose 和 Flutter 分别是挂在 Android 宿主里的两种内容 runtime,但它们挂载的位置、对窗口/渲染的接管范围并不一样。
这张图里最关键的是三层边界:
- Android App / Activity / Fragment 是宿主层级
- 一句话答:它们决定进程内页面挂载点、窗口归属、返回栈、配置变更与生命周期分发,Compose 和 Flutter 都不能绕开这一层。
- Compose 运行在 Android ViewSystem 之内
- 一句话答:Compose 通过
ComposeView或setContent挂进现有 View/Window 体系,生命周期和窗口仍由 Activity/Fragment/ViewTree 管,Compose 主要接管的是内容描述与最小更新。
- 一句话答:Compose 通过
- Flutter 运行在 Android 宿主之上但自带 engine/runtime
- 一句话答:Flutter 会把
FlutterView挂到 Android 页面里,再由FlutterEngine+ Dart isolate 负责自身 UI 树和渲染线程;它接管的是内容 runtime,不是 Android 的窗口与宿主生命周期。
- 一句话答:Flutter 会把
对混合栈判断最有用的收束可以记成一句话:Android 宿主边界通常止于 Activity / Fragment / Window / View 挂载点;Compose 边界大致止于 Compose 内容树;Flutter 边界大致止于 FlutterView 之内的 engine + Dart UI 内容。
4. XML View 体系 vs Jetpack Compose:不是换语法,而是换运行时架构
相关主题: Android View 绘制流程、Choreographer 与 VSync 帧同步 · Jetpack Compose 状态模型、StateFlow 与重组优化
很多团队会把 Compose 说成“少写 XML、写法更现代”,这个说法太浅。真正重要的是:Compose 把 UI 更新模型从“操作现有 View 对象”改成了“根据状态重新计算 UI 描述,并让 runtime 决定最小更新范围”。
4.1 总结对比表
| 维度 | XML View 体系 | Jetpack Compose | 你该如何理解 |
|---|---|---|---|
| 编程范式 | 命令式 / 对象式 | 声明式 / 状态驱动 | 一个是在改树上的对象,一个是在重算 UI 描述 |
| UI 载体 | View 树 / ViewGroup 层级 | Composition + Slot Table + LayoutNode | View 是真实对象树;Compose 运行时维护组合结构与记忆表 |
| 更新入口 | findViewById、属性 setter、invalidate/requestLayout | 状态变化触发 recomposition | View 靠显式改对象;Compose 靠状态驱动重新执行受影响函数 |
| 脏区传播 | 依赖 invalidate、requestLayout、遍历树 | 依赖 snapshot 读写追踪、stability、skip/recompose | View 更偏“树遍历”,Compose 更偏“订阅哪些状态、重跑哪些节点” |
| 状态边界 | 容易散落在 View、Adapter、Fragment 字段 | 倾向提升到 remember / rememberSaveable / ViewModel | Compose 强迫你更明确区分瞬时状态与页面状态 |
| 渲染边界 | ViewRootImpl 的 measure/layout/draw | Composition → Layout → Draw | 两者最终都受 VSync 驱动,但中间 runtime 不同 |
| 可测试性 | UI 与对象副作用常耦合 | Stateless Composable 更容易拆分和预览 | Compose 更鼓励状态提升和纯渲染函数 |
4.2 一张示意图看出差异
这张图有三个核心判断:
- View 体系的第一反应是“改对象”
- 一句话答:页面已经有一棵真实 View 树,更新通常是拿到某个 View/Adapter 后直接改属性,再请求系统重绘或重排。
- Compose 的第一反应是“改状态”
- 一句话答:你通常不去“命令一个 Text 变红”,而是改状态,让读取该状态的 Composable 在下一帧自动重组。
- 两者最终都要进入布局和绘制,但更新前的中间层完全不同
- 一句话答:View 依赖对象树与遍历;Compose 依赖 runtime 对状态读取关系、slot table 和记忆节点的维护。
4.3 进一步拆:View 树 vs Compose runtime / slot table / recomposition
View 树心智
View 体系的经典思路是:
- inflate XML 得到对象树。
- 每个 View 持有自己的状态与属性。
- 业务代码通过引用直接修改这些对象。
- 通过
invalidate()/requestLayout()让 ViewRootImpl 在下一帧重新遍历。
这种模式的优势是对象直观、调试简单、与平台 API 强绑定;缺点是状态容易散、局部刷新边界不稳定、复杂页面常出现 Adapter / View / Fragment / DataBinding 的耦合泥球。
Compose runtime 心智
Compose 则换了问题定义:
- Composable 函数描述“当前状态下 UI 应该长什么样”。
- runtime 在组合时记录“谁读取了哪些状态”。
- 状态变更后,Recomposer 只让受影响节点重新执行。
- Slot table 负责记忆
remember等组合位置相关数据。
这里最容易被忽略的是 slot table 的角色:它不是“另一棵 View 树”,而是 Compose runtime 维护的组合记忆表,用来记住当前组合结构、remember 的值、节点位置以及可跳过/需重组的信息。
渲染边界差异
- 在 View 里,边界通常是某个 View 或某片脏区域是否需要重新 measure/layout/draw。
- 在 Compose 里,边界首先是某个 Composable 是否因为读取到的状态变化而需要重新执行;之后才进入 layout/draw。
所以 Compose 优化常见关注点会变成:
- 这个状态是不是读得过宽,导致整个大 Composable 都重组?
- 这个对象是否稳定(stable/immutable),能否跳过无意义重组?
- 这份状态该不该放
remember、rememberSaveable、ViewModel,还是只做临时 UI state?
5. Android 架构里最值得优先建立的几组“交叉索引”
这一页本身不替代专题,而是给你建立索引关系。建议把下面几组组合一起复习:
5.1 执行与阻塞线
这组回答:
- 主线程怎么跑
- 跨进程怎么调
- 为什么会 ANR
- 为什么很多“偶发卡死”其实是 Binder、锁、主线程消息队列交叉阻塞
5.2 生命周期与状态线
这组回答:
- 宿主什么时候重建
- 页面状态应活到哪一层
ViewModel、SavedStateHandle、Compose state 的边界怎么分- Flutter 工程师最容易把“页面 state”和“宿主恢复 state”混在哪
5.3 启动与渲染线
- Android App 进程启动全流程 (Zygote → ActivityThread → 首帧)
- Android View 绘制流程、Choreographer 与 VSync 帧同步
- 应用启动性能优化实战(拓扑排序、异步初始化与首帧治理)
这组回答:
- 进程怎么启动
- 首帧为什么慢
- VSync / Choreographer / measure-layout-draw 在哪接起来
- 启动优化为什么必须同时看 Application、Provider、主线程、首帧与渲染
5.4 与 Flutter 的宿主对照线
- Flutter 引擎线程模型:UI isolate / raster / platform / IO
- Compose / Flutter / Vue 状态管理对照
- PlatformChannel 深度解析:MethodChannel / EventChannel / BasicMessageChannel
这组回答:
- Flutter engine 线程和 Android 主线程怎么对照
- 状态驱动 UI 在 Compose/Flutter 里哪里相似、哪里不一样
- Platform Channel 为何最终还是回到 Android 宿主能力边界
Android / Flutter / Web / Backend 对照
这里只保留对主线理解有帮助的对照,不做平均铺开。
| 维度 | Android | Flutter(运行在 Android 上时) | WebView/H5 | Backend |
|---|---|---|---|---|
| 宿主平台 | Android OS + Framework + ART | 仍是 Android 宿主,只是 UI runtime 换成 Flutter engine + Dart | Android 宿主 + 浏览器内核 | 通常无 UI 宿主,主要是进程/容器/网络协议 |
| 主执行模型 | 主线程 + Looper + Binder + 生命周期回调 | UI isolate + engine threads,但仍受 Android Activity/Window/插件边界约束 | 浏览器 JS event loop + Android 宿主生命周期 | 线程池 / 协程 / 事件循环,少了 UI 帧驱动 |
| UI 架构 | XML View / Compose | Widget / Element / RenderObject | DOM / CSSOM / JS | 无 |
| 系统能力接入 | 直接 Framework / Binder API | Platform Channel → Android 原生 | JSBridge / Web APIs + 宿主注入 | 系统调用 / 中间件 / RPC |
| 常见误判 | 只会看页面 API,不会回到底层分层 | 以为 Flutter 屏蔽了宿主问题 | 以为 H5 问题完全与宿主无关 | 把服务端模型直接类比 UI 生命周期 |
常见场景
1. 你在做一个混合栈 App,为什么要先知道“当前问题落在哪层”
真实工程里最浪费时间的不是不会修,而是修错层:
- 页面白屏,实际是启动链上某个 Provider 或主线程初始化拖慢。
- Flutter 页面卡顿,实际是平台通道或原生主线程阻塞。
- WebView 登录态错乱,实际是宿主 token 同步与 JSBridge 契约有洞。
- Compose 页面频繁重组,实际是状态边界放错,不是“Compose 天生性能差”。
读完这页后,最起码要能先问自己:
- 这是应用层问题、Framework 问题、Runtime 问题,还是 Native/Kernel 边界问题?
- 是线程堵了、Binder 堵了、生命周期边界错了,还是渲染 runtime 自己的状态建模出了问题?
2. 你要解释“为什么 Android 能容纳这么多技术形态”
一个对外表达很常见的场景是架构评审或面试。更准确的回答不是:
- Android 能跑 Java/Kotlin,因为有 JVM。
- Android 能跑 Flutter,因为有插件。
而应是:
- Android 作为宿主平台,提供了 组件模型、窗口系统、系统服务、权限、进程隔离、输入与渲染接入点。
- Java/Kotlin 是默认托管层。
- Native 是性能/系统能力层。
- WebView/Flutter 是嵌入式执行与渲染容器。
- 它们并列存在,但最终共用同一宿主边界。
3. 你在做 XML View → Compose 迁移时,该怎么判断“改的是语法还是架构”
如果迁移时你只是:
- 把 XML 改成 Composable
- 但状态仍散落在 Fragment 字段、Adapter、单例、回调里
- 仍然习惯事件直接推 View,而不是让状态驱动 UI
那你改的只是语法皮,没有真正迁移架构。
更接近 Compose 正确迁移的信号是:
- 页面状态被显式建模
- Stateless UI 比例提高
- 事件与状态分离
- 页面级状态与宿主恢复状态分层明确
- 重组边界和性能分析开始围绕“谁读了哪些状态”展开
常见误配、事故后果与排障
| 误区 | 典型后果 | 排障切入点 | 修法 |
|---|---|---|---|
| 把 Android 只当“Java UI 框架” | 解释不了 Binder、ANR、启动、WebView/Flutter 宿主问题 | 先定位问题落在哪层 | 用“App / Framework / Runtime / Native / Kernel”重建分析框架 |
| 把 Looper、生命周期、渲染看成互不相干 | 页面卡顿、重复请求、状态丢失时只会局部修补 | 看主线程消息、生命周期回调与首帧时序是否串起来 | 以主线程执行现场为主线串起来分析 |
| 认为 Flutter/Compose 屏蔽了宿主边界 | 平台调用卡顿、生命周期错位、恢复失败 | 看宿主 Activity、Platform Channel、线程模型 | 明确“新 UI runtime 不会抹掉 Android 宿主” |
| 把 Compose 只理解成“少写 XML” | 迁移后代码更乱、重组失控 | 看状态分层、重组范围、remember/ViewModel 边界 | 按声明式 runtime 重建状态模型 |
| 不区分 Runtime 与 Framework | GC、启动、ClassLoader、JNI 问题定位模糊 | 看问题是 API 契约层,还是执行环境层 | 用“Framework 提供规则,Runtime 提供执行环境”来拆 |
与相近概念对比
| 概念 | 更准确的边界 |
|---|---|
| Android Framework | 系统对应用暴露的 API、组件模型、窗口与服务契约 |
| Android Runtime (ART) | Java/Kotlin 字节码执行、GC、ClassLoader、JNI 承载环境 |
| Native 层 | Binder、图形、媒体、数据库、WebView 内核、系统 C/C++ 能力 |
| XML View | 命令式对象树 UI 系统 |
| Jetpack Compose | 声明式状态驱动 UI runtime |
| Flutter on Android | 自带 Dart + engine 的 UI runtime,运行在 Android 宿主中 |
| WebView | 嵌入 Android 页面中的浏览器执行容器 |
对应实验
本页当前作为总览索引页,不新增伪实验。请直接跳到已有专题对应的真实 lab:
| 文档 | 说明 | 对应实验 |
|---|---|---|
| Android 主线程消息循环:Looper / Handler / MessageQueue | 主线程消息循环 / HandlerThread | looper-handler-demo |
| Android Binder IPC 机制与跨进程通信 | Binder / Messenger / 跨进程通信 | binder-messenger-demo |
| Android ANR 触发机制、排障定位与防范 | 主线程阻塞 / ANR 诊断 | anr-mechanism-demo |
| Android 生命周期总览 | 生命周期 / 配置变更 / 进程恢复 | android-activity-lifecycle-basic 等 |
| ViewModel 架构、SavedStateHandle 与进程被杀恢复 | ViewModel / SavedStateHandle | viewmodel-savedstate-demo |
| 应用启动性能优化实战(拓扑排序、异步初始化与首帧治理) | 启动打点与优化 | startup-timing-demo |
复习检查题
为什么说 Android 不是“一个 Java UI SDK”,而是一个分层平台?
答:因为 App 层只是最上面一层;下面还有 Framework 提供组件模型与系统 API,ART 提供 Java/Kotlin 执行环境,Native 提供 Binder/图形/媒体/WebView 等核心能力,Kernel 再提供线程调度、内存和驱动。实际工程问题常常跨越多层,不能只盯着应用代码。
线程、Looper、生命周期三者各自回答什么问题?
答:线程回答“谁在执行”;Looper/MessageQueue 回答“主线程按什么顺序串行执行消息”;生命周期回答“Framework 什么时候让你的组件进入哪些状态并执行哪些回调”。三者在主线程上汇合成 Android 页面的真实运行现场。
为什么 Android 能同时承载 Java/Kotlin、Native、WebView 和 Flutter?
答:因为 Android 既提供托管运行时(ART),也提供 Native 能力(JNI/NDK + 系统库),还提供可嵌入容器(WebView、FlutterEngine)接入窗口、输入、生命周期与系统服务。不同技术形态可以共存,但最终仍依附同一个宿主平台边界。
XML View 和 Compose 的最核心架构差异是什么?
答:View 体系是命令式对象树,更新靠修改 View 对象属性并触发
invalidate/requestLayout;Compose 是声明式 runtime,更新靠状态变化触发 snapshot 追踪下的 recomposition,由 runtime 决定哪些组合节点重新执行。它们不是只差“写法”,而是更新模型与运行时结构都不同。为什么 Flutter 工程师仍然需要理解 Android 宿主架构?
答:因为 Flutter 在 Android 上只是替换了 UI runtime,并没有替换宿主平台本身。
FlutterActivity、Platform Channel、权限、窗口、生命周期、Binder、启动与 ANR 仍然属于 Android 宿主边界;不理解这些边界,就解释不了大量真实线上问题。
速记
- Android = App 层 + Framework + ART + Native + Kernel,不是单一 UI SDK
- 线程回答“谁跑”,Looper 回答“怎么排队跑”,生命周期回答“系统何时让你跑”
- Java/Kotlin、NDK、WebView、Flutter 能共存,因为它们都挂在同一宿主平台上
- View 是命令式对象树;Compose 是声明式状态驱动 runtime
- Flutter 替换的是 UI runtime,不是 Android 宿主底座