文章目录
- 学习建议
- [基础层(7 题)](#基础层(7 题))
-
- [#1 Flutter 如何保持 60fps? 🔥](#1 Flutter 如何保持 60fps? 🔥)
- [#2 UI 线程和 Raster 线程分工? 🔥](#2 UI 线程和 Raster 线程分工? 🔥)
- [#3 什么是 jank?如何感知? 🔥](#3 什么是 jank?如何感知? 🔥)
- [#4 `SchedulerBinding` 与帧回调? ⭐](#4
SchedulerBinding与帧回调? ⭐) - [#5 Skia 在 Flutter 中的作用? ⭐](#5 Skia 在 Flutter 中的作用? ⭐)
- [#6 Impeller 是什么? ⭐](#6 Impeller 是什么? ⭐)
- [#7 DevTools 性能页看什么? 🔥](#7 DevTools 性能页看什么? 🔥)
- [进阶层(7 题)](#进阶层(7 题))
-
- [#8 一帧内 build/layout/paint 顺序? 🔥](#8 一帧内 build/layout/paint 顺序? 🔥)
- [#9 Layer 与合成怎么做? ⭐](#9 Layer 与合成怎么做? ⭐)
- [#10 列表性能如何优化? 🔥](#10 列表性能如何优化? 🔥)
- [#11 图片与内存优化? 🔥](#11 图片与内存优化? 🔥)
- [#12 `const` 与重建优化? ⭐](#12
const与重建优化? ⭐) - [#13 isolate 何时用? ⭐](#13 isolate 何时用? ⭐)
- [#14 首屏与启动优化? ⭐](#14 首屏与启动优化? ⭐)
- [#15 Shader 编译 jank 是什么? 💡](#15 Shader 编译 jank 是什么? 💡)
- [核心层(6 题)](#核心层(6 题))
-
- [#16 如何系统排查一次 jank? 🔥](#16 如何系统排查一次 jank? 🔥)
- [#17 Flutter 渲染与 Android Display Pipeline 关系? 💡](#17 Flutter 渲染与 Android Display Pipeline 关系? 💡)
- [#18 `PerformanceOverlay` 看什么? ⭐](#18
PerformanceOverlay看什么? ⭐) - [#19 内存泄漏常见原因? 🔥](#19 内存泄漏常见原因? 🔥)
- [#20 包体积与运行时性能? ⭐](#20 包体积与运行时性能? ⭐)
- [#21 FFI 与性能? 💡](#21 FFI 与性能? 💡)
- [#22 生产环境监控怎么做? 💡](#22 生产环境监控怎么做? 💡)
- 面试策略速查
- 完整链路一句通
- 相关推荐
学习建议
| 目标层级 | 建议 |
|---|---|
| 初级 | 掌握基础层 7 题:60fps/16.6ms 预算、UI/raster 线程分工、jank 概念与感知方式、DevTools 基础使用 |
| 中级 | 通读全文;进阶层能讲清一帧内 build/layout/paint 顺序、Layer 合成机制、Impeller vs Skia 选型、列表与图片优化策略 |
| 高级 | 核心层必答:系统化 jank 排查流程、与 Android Display Pipeline 的关系、内存泄漏常见根因、isolate 与 FFI 使用场景;能独立定位 build/layout/raster 瓶颈 |
基础层(7 题)
#1 Flutter 如何保持 60fps? 🔥
标准回答
显示刷新率通常为 60Hz(高刷屏 90/120Hz),对应每帧预算约 16.6ms (120Hz 约 8.3ms)。引擎在 vsync 信号驱动下完成一帧:Dart 侧执行 build → layout → paint 录制 DisplayList/LayerTree → 交给 Raster 线程栅格化 → 提交 GPU 合成上屏。任一阶段超时即产生 jank(掉帧)。优化方向:减少 build/layout 耗时、降低 raster 压力、控制过度绘制、避免主 isolate 被同步操作阻塞。
面试官可能继续追问
-
Android 60fps 机制对照?
同样由 VSYNC 信号驱动;Flutter 多一层 Dart → Engine 的跨语言调用,瓶颈可能出现在 UI 线程或 Raster 线程。
-
高刷屏手机 Flutter 能跟上吗?
需引擎支持高刷模式;帧预算更紧(120Hz 仅 8.3ms),对 build/raster 耗时要求更苛刻。
#2 UI 线程和 Raster 线程分工? 🔥
标准回答
UI 线程 (运行 Dart isolate):执行 Widget build、RenderObject layout、paint 录制,产出 DisplayList 和 LayerTree。Raster 线程 (GPU 线程):接收 LayerTree,执行栅格化(将矢量指令转为像素)、纹理上传、与 GPU 命令提交。二者通过 SceneBuilder 和 LayerTree 进行 handoff。UI 线程卡顿影响逻辑响应;Raster 线程卡顿影响画面呈现。DevTools Performance 页面分别展示 UI Time 与 Raster Time,排查时先确定瓶颈在哪一侧。
面试官可能继续追问
-
Android main thread vs RenderThread 对照?
极其相似:主线程负责 measure/layout/draw 录制,RenderThread 负责光栅化;Flutter 引擎映射了这一模型。
-
能在 Raster 线程跑 Dart 代码吗?
不能。Dart 代码始终在 UI isolate 执行,重计算任务应通过
compute或Isolate.spawn移至后台 isolate。
#3 什么是 jank?如何感知? 🔥
标准回答
Jank :某一帧未能在 vsync 信号到来前完成全部渲染工作,导致画面跳过该帧,用户感知为卡顿、跳动或动画不流畅。排查手段:DevTools Performance 页面查看帧时间条,超出红线即为 jank 帧;debugPrintRebuildDirtyWidgets 可辅助定位频繁重建的 Widget。Android 侧可用 Systrace/Perfetto 对齐观察。开发调试时开启 Repaint Rainbow 可直观看到重绘区域,辅助判断重绘范围是否过大。
面试官可能继续追问
-
Android Systrace 能看 Flutter 帧吗?
可以。同时开启 DevTools Timeline 与 Systrace,对齐 UI/Raster 时间段可做联合分析。
-
偶发 jank 和持续 jank 原因有何不同?
偶发 jank 常见于 GC 触发、首帧初始化、Shader 编译;持续 jank 通常是 build 范围过大或列表未懒加载等系统性问题。
#4 SchedulerBinding 与帧回调? ⭐
标准回答
SchedulerBinding 是 Flutter 帧调度的核心,注册 vsync 回调并管理帧生命周期。每帧按顺序触发三个阶段:transientCallbacks (动画 tick,如 Ticker 驱动 AnimationController)→ persistentCallbacks (核心 drawFrame,执行 build/layout/paint 产出 LayerTree)→ postFrameCallbacks (帧完成后一次性回调)。scheduleFrame() 用于请求引擎在下一 vsync 时触发新帧。动画状态变更与 setState 最终都汇聚到这一帧管道统一提交。
面试官可能继续追问
-
Android Choreographer 对照?
同样由 vsync 驱动帧回调;Flutter 在 Dart 层再次封装调度器,提供更细粒度的回调阶段。
-
addPostFrameCallback典型用途?首帧渲染完成后获取 Widget 尺寸、显示 SnackBar、执行滚动定位等需要在布局确定后才能进行的操作。
#5 Skia 在 Flutter 中的作用? ⭐
标准回答
Skia 是开源 2D 图形库,负责将 LayerTree 中的绘制指令(路径、文字、渐变、阴影等)光栅化为像素。Flutter Engine 将框架层产出的 LayerTree 交给 Skia(或 Impeller),后者生成 GPU 命令完成实际绘制。Skia 作为跨平台图形后端,为 Flutter 提供了"自绘"能力,保证 iOS 和 Android 的像素级一致性。传统 Android HWUI 渲染管线内部也曾使用 Skia 作为光栅化后端。
面试官可能继续追问
-
Android HWUI 还走 Skia 吗?
录制显示列表后,底层光栅化曾使用 Skia;现代版本逐步转向 GPU 硬件加速路径,但概念相似。
-
Flutter 就是 Skia 应用吗?
不完全是。Flutter 在 Skia 之上还有完整的框架层(Widget → Element → RenderObject),负责布局算法、合成编排、命中测试等。
#6 Impeller 是什么? ⭐
标准回答
Impeller 是 Flutter 新一代渲染后端,核心优势是预编译着色器(AOT shader compilation),消除 Skia 运行时 JIT 编译着色器导致的"shader compilation jank"。底层优先使用 Metal(iOS)和 Vulkan(Android)等现代图形 API。iOS 端已默认启用,Android 端持续推广中。面试要点:Impeller 主要解决首帧和复杂特效场景下 shader 编译的尖刺卡顿,但不是万能性能银弹------build/layout 耗时问题仍需开发者自行优化。
面试官可能继续追问
-
Android 端如何开启 Impeller?
根据 Flutter 版本,通过
AndroidManifest.xml配置或flutter run --enable-impeller标志启用;具体参考 SDK 发行说明。 -
Impeller 和 Skia 能并存吗?
过渡期内平台可二选一;长期趋势是 Impeller 成为主力后端,Skia 逐步退出。
#7 DevTools 性能页看什么? 🔥
标准回答
Frame Chart :区分 UI 耗时与 Raster 耗时,快速判断瓶颈侧。Timeline 事件 :展开每帧查看 build、layout、paint 各阶段耗时分布。CPU Profiler :定位 Dart 代码热点函数。Memory :监控对象数量与图片缓存占用,发现泄漏。Widget rebuild stats:统计各 Widget 重建次数与频率。排查方法论:先确认 jank 发生在 UI 还是 Raster → 再下钻到具体阶段 → 定位到具体 Widget 或图片 → 针对性优化。
面试官可能继续追问
-
和 Android Studio Profiler 对比?
CPU/Memory 功能类似;Flutter DevTools 额外提供 Dart 特有的 Timeline 事件和 Widget 重建统计。
-
为什么必须用 Profile 模式?
Debug 模式包含断言检查和 JIT 编译开销,性能数据严重失真;Profile 模式接近真实发布表现。
进阶层(7 题)
#8 一帧内 build/layout/paint 顺序? 🔥
标准回答
drawFrame 方法内严格按序执行:build (遍历 dirty Element 重建 Widget 配置,产出新的 Element 树)→ flushLayout (自底向上计算 RenderObject 尺寸与位置约束)→ flushCompositingBits (更新合成标记)→ flushPaint(将 RenderObject 绘制指令录制到 Layer 树)→ 提交 LayerTree 给 Raster 线程合成。注意:若仅有颜色等非几何属性变化,layout 阶段可能被跳过以节省开销。理解此顺序是定位性能瓶颈的基础。
面试官可能继续追问
-
Android measure/layout/draw 对照?
流程高度对应:Flutter 将其合并在单次引擎帧回调中,而非分散在多个遍历。
-
setState调用后立刻看到像素变化吗?不会。
setState仅标记 Element 为 dirty,真正的 build → paint → raster 需等下一个 vsync 周期。
#9 Layer 与合成怎么做? ⭐
标准回答
RenderObject 的 paint 方法可将绘制指令录制到不同类型的 Layer 对象(OffsetLayer 位移、TransformLayer 矩阵变换、OpacityLayer 透明度、ClipRectLayer 裁剪等),最终形成一棵 Layer 树提交给引擎合成。RepaintBoundary 将子树提升为独立 Layer,当子树内容变化时仅重绘该 Layer 而无需重绘父级,是实现局部重绘的关键机制。但 Layer 并非越多越好:过多 Layer 增加内存占用与合成阶段的 GPU 计算成本。Opacity 等操作可能触发 saveLayer(离屏渲染),性能代价较高。
面试官可能继续追问
-
Android Hardware Layer 对照?
类似
View.setLayerType(LAYER_TYPE_HARDWARE, ...)创建离屏缓存;Flutter 通过RepaintBoundary显式控制。 -
Opacity 为什么性能代价高?
当
alpha != 1.0且子节点有重叠时,Flutter 需创建saveLayer离屏缓冲区进行混合,增加显存与填充开销。
#10 列表性能如何优化? 🔥
标准回答
构建策略 :务必使用 ListView.builder 或 SliverList 实现按需懒加载,禁止 在大列表中使用 ListView(children: [...]) 全量构建。shrinkWrap 会让列表一次性测量所有子项,应仅在小列表或特殊嵌套场景使用。稳定 Key :为列表项提供 ValueKey(id),帮助 Framework 正确复用 Element,避免重建错位。项内优化 :Item Widget 尽可能使用 const 构造函数;将每个 Item 包裹 RepaintBoundary 隔离重绘范围。状态粒度 :列表数据变更用 Item 级 Provider 或 itemProvider(id),避免整列表重建。图片 :指定 cacheWidth/cacheHeight 限制解码尺寸。分页加载:预取下一页数据,减少首屏等待。
面试官可能继续追问
-
Android RecyclerView 对照?
ListView.builder的懒构建机制等同 RecyclerView 的 ViewHolder 复用;无 Key 时类似position复用导致的错乱。 -
itemExtent有什么价值?指定固定项高度可让列表跳过测量阶段直接布局,大幅提升滚动流畅度。
#11 图片与内存优化? 🔥
标准回答
解码尺寸控制 :Image.network 配合 cacheWidth/cacheHeight,让引擎解码为指定尺寸而非原始分辨率,避免内存浪费。预加载 :precacheImage 在页面进入前提前加载关键图片。缩略图 :列表缩略图用 ResizeImage 指定解码尺寸,原图仅详情页加载。缓存配置 :ImageCache 默认最多 1000 张、100MB,可通过 PaintingBinding.instance.imageCache 调整,但不宜过大。格式选择 :透明 PNG 占用内存远大于 WebP/HEIC;优先使用压缩格式。释放 :离开页面时取消 ImageStream 监听,避免未显示的图片持续占用内存。
面试官可能继续追问
-
Android Glide 对照?
Flutter 内置
ImageCache+PaintingBinding提供基础能力;复杂缓存策略可用cached_network_image插件。 -
OOM 怎么排查?
DevTools Memory 页面查看
Image对象数量与ImageCache当前占用;排查是否有原图直接显示或缓存未释放。
#12 const 与重建优化? ⭐
标准回答
const 构造函数使 Widget 实例在编译期确定,运行时同一 const Widget 始终是同一个实例,Framework 通过 identical 比较即可判定无需重建,跳过该子树整个 build 流程。最佳实践:将变化部分隔离到最小 StatefulWidget 叶子节点;AnimatedBuilder 的 builder 回调仅包裹动画依赖的子树。Riverpod 的 select 或 Bloc 的 buildWhen 从数据层面减少不必要重建。常见破坏优化 :在 build 方法内创建非 const 对象(DateTime.now()、Random()、闭包内 new List),导致每次 build 都产生新实例。
面试官可能继续追问
-
Android Compose Skipping 对照?
同是跳过无变化 Composable 的重组;Flutter 依赖
const+ Element 复用机制。 -
build里调用DateTime.now()有什么问题?值每次变化,父级重建时子 Widget 参数不同,导致即便业务逻辑无变化也被迫重建。
#13 isolate 何时用? ⭐
标准回答
UI isolate 是单线程事件循环模型,任何同步耗时操作都会阻塞帧回调导致 jank。compute 函数 适合单次 CPU 密集型任务(JSON 解析、加密解密、图片压缩),内部自动创建 isolate 并在完成后返回结果。Isolate.spawn 适合需要长期运行的后台任务,通过 SendPort/ReceivePort 与 UI isolate 双向通信。重要约束:isolate 之间不共享内存,传递数据需序列化拷贝(大对象成本较高);IO 操作(网络、文件)用 async/await 即可,无需 isolate。不能在后台 isolate 中调用 Platform Channel 或操作 Widget。
面试官可能继续追问
-
Android 后台线程对照?
isolate 类似
Thread+ 消息队列,但无共享内存,天然避免锁竞争。 -
isolate 能调 Channel 或更新 UI 吗?
都不能。计算结果通过
SendPort发回 UI isolate,由 UI isolate 调 Channel 或setState更新。
#14 首屏与启动优化? ⭐
标准回答
引擎预热 :Application 中预先创建 FlutterEngine 并放入 FlutterEngineCache,Activity 启动时直接取用。延迟初始化 :非首屏必需的插件和模块延后初始化。简化首屏 :首屏使用骨架屏或静态占位,减少首帧 Widget 复杂度。资源优化 :字体子集化(仅打包用到的字符)、图片使用压缩格式。编译优化 :Release 构建关闭 Debug 检查;Tree Shaking 自动移除未使用代码。Android 端配合 :原生 SplashScreen 作为启动过渡,掩盖引擎初始化白屏。测量指标:用 DevTools Timeline 追踪 TTFP(Time to First Pixel)。
面试官可能继续追问
-
Android Baseline Profile 对 Flutter 有用吗?
Flutter 3.16+ 支持在 Android 侧生成 AOT 编译指导文件,配合 Baseline Profile 优化启动速度。
-
白屏时间长的主要瓶颈?
引擎初始化耗时 + 首帧 build/raster;预热引擎 + 减首屏复杂度是最有效手段。
#15 Shader 编译 jank 是什么? 💡
标准回答
Skia 在首次遇到复杂绘制效果(渐变、模糊、复杂路径、BoxShadow)时,需要在运行时 JIT 编译对应的 GPU Shader 程序 ,该编译过程耗时可达数十到上百毫秒,导致该帧 UI 或 Raster 线程出现尖刺卡顿,即 "shader compilation jank"。Impeller 解决思路 :所有 Shader 在构建时预编译(AOT),运行时直接加载,消除编译等待。Skia 时代的缓解手段:用 ShaderWarmUp 在启动后空闲时预绘制常见效果触发编译;首屏尽量避免复杂特效;录制关键路径的 SkSL 预热文件并在生产环境回放。此问题在 Android 端尤为明显,因 GPU 驱动和硬件组合多样。
面试官可能继续追问
-
Android 原生有类似问题吗?
存在,但程度较轻。Android 驱动层有 shader cache 机制;Flutter 因跨端统一特效导致 shader 变体更多。
-
如何录制 SkSL 预热文件?
在 Profile 模式下跑关键路由,引擎生成
.sksl文件,打包到应用中并在启动时加载回放。
核心层(6 题)
#16 如何系统排查一次 jank? 🔥
标准回答
系统化排查流程 :1)DevTools Performance 定位 jank 帧,先看 UI Time 还是 Raster Time 超线;2)在 Timeline 中展开该帧,找到耗时最长的 BUILD/LAYOUT/PAINT 事件;3)如果 BUILD 慢 → 检查是否同步 IO、大范围 Widget 重建、build 内重计算;4)如果 LAYOUT 慢 → 检查是否深层嵌套、shrinkWrap 列表、多遍布局(IntrinsicHeight 等);5)如果 PAINT 慢 → 检查 Opacity/saveLayer、过度绘制、未使用 RepaintBoundary;6)如果 Raster 慢 → 检查图片解码尺寸、复杂 Shader、过多 Layer 合成。每次只改一个变量,改后 Profile 验证,避免盲目加 RepaintBoundary。
面试官可能继续追问
-
Android Perfetto 能联合分析吗?
可以。Perfetto 抓取系统级 VSYNC 信号与 Flutter Engine 的 trace marker,可做端到端对齐。
-
Release 包才卡、Debug 不卡?
检查是否 Debug 模式下跳过了某些重计算逻辑(如
assert包裹的代码)或网络返回数据量差异。
#17 Flutter 渲染与 Android Display Pipeline 关系? 💡
标准回答
Flutter Engine 在 Android 上作为 Surface Producer 运行,产出的每帧 buffer 通过 ANativeWindow 提交给 SurfaceFlinger 进行系统级合成。Dart 侧的自绘完全绕过了 Android View hierarchy(ViewGroup → View → Canvas),不经过 HWUI 渲染栈。在混合应用中,Flutter 的 Surface 与原生 View 的 Surface 作为不同的 Layer 由 SurfaceFlinger 统一合成上屏。理解此关系有助于分析混合场景下的帧同步问题,以及 PlatformView 额外合成开销的来源。
面试官可能继续追问
-
PlatformView 有什么特殊性?
原生 View 被渲染到独立纹理,再与 Flutter Layer 混合合成,存在额外的纹理拷贝和合成开销。
-
Flutter 和 Compose 谁更"原生"?
Compose 走 Android 渲染栈(最终仍是 SurfaceFlinger 合成);Flutter 使用独立引擎,绕过 Android View 体系但合成点相同。
#18 PerformanceOverlay 看什么? ⭐
标准回答
PerformanceOverlay 开启后在屏幕顶部角落叠加显示两条水平时间条:上方为 GPU(Raster)线程耗时 ,下方为 UI 线程耗时 。绿色表示当前帧在 16ms 基准线内完成,红色表示超时掉帧。用途:无需打开 DevTools 即可快速判断瓶颈在 UI 侧还是 Raster 侧;页面切换时实时观察帧率变化。仅限 Debug/Profile 模式使用,不可带到 Release 构建。配合 debugProfileBuildsEnabled 等调试 flag 可获取更细粒度的 build 耗时信息。
面试官可能继续追问
-
Android 的 GPU 呈现模式分析?
类似开发者选项中的"GPU 渲染配置文件",以柱状图显示每帧耗时。
-
两条线都红说明什么?
UI 和 Raster 双向瓶颈,通常先优化 UI build 减少 Layer 产出,再处理 Raster 侧问题。
#19 内存泄漏常见原因? 🔥
标准回答
动画与控制器 :AnimationController 创建后未在 dispose() 中调用 dispose()。Stream 订阅 :StreamSubscription 未在页面销毁时 cancel()。ChangeNotifier 监听 :手动 addListener 未在 dispose 中 removeListener(Provider 框架通常自动管理,手写需注意)。Context 持有 :静态变量或长生命周期对象持有 BuildContext,导致整个 Element 树无法释放。GlobalKey 滥用 :持有 GlobalKey 的对象未释放导致对应 Widget State 常驻。图片流 :ImageStream 添加监听后未移除。混合工程特有 :Channel handler 持有 Activity 引用,Activity 重建后旧 handler 未清理。排查工具:DevTools Memory 页面做前后快照对比;leak_tracker 包自动检测。
面试官可能继续追问
-
Android LeakCanary 能用于 Flutter 吗?
原生侧仍可用 LeakCanary 检测 Java/Kotlin 泄漏;Dart 侧使用 DevTools + 规范
dispose。 -
引擎缓存导致的泄漏?
Channel handler 持有 Activity 引用是最常见场景;
onDestroy中必须setMethodCallHandler(null)。
#20 包体积与运行时性能? ⭐
标准回答
体积优化 :flutter build apk --split-per-abi 按 ABI 分包,单个包仅包含对应架构的 .so 库;移除未使用的字体文件和资源;--obfuscate + --split-debug-info 混淆并分离调试信息。动态分发 :Android App Bundle(AAB)让 Google Play 按设备 ABI 分发;Deferred Components 实现 Dart 代码的按需下载。性能关系:包体积主要影响下载转化率和安装速度,对帧率无直接影响;但 Dart 代码量会影响 AOT 编译产物体积与加载时间。勿为体积牺牲必要的性能优化(如关 Tree Shaking)。
面试官可能继续追问
-
Flutter 支持 Android App Bundle 吗?
完全支持;Play 商店自动按设备架构分发对应 ABI 的 .so 文件。
-
Impeller 会增加包体积吗?
视版本而定,Impeller 自带预编译 shader 库;需权衡包体积增量与 Shader 编译 jank 消除的收益。
#21 FFI 与性能? 💡
标准回答
Dart FFI (Foreign Function Interface)允许 Dart 代码直接调用 C/C++ 编译的 .so 动态库,绕过 Platform Channel 的序列化开销和线程切换。适合场景:音视频编解码、加密算法、游戏物理引擎、图像处理等计算密集任务。与 Channel 的关系是互补:系统 API 调用(相机、定位)走 Channel/Plugin;纯计算密集任务走 FFI。使用注意:需自行管理 native 内存与线程安全;FFI 调用可在后台 isolate 执行(须注意 native 代码的线程安全性)。
面试官可能继续追问
-
Android JNI 对照?
FFI 是 Dart 侧的 JNI 等价物,概念和约束高度相似。
-
FFI 能和 isolate 组合使用吗?
可以。FFI 调用在 isolate 中执行,避免阻塞 UI isolate;但 native 侧必须保证线程安全。
#22 生产环境监控怎么做? 💡
标准回答
APM 集成 :Firebase Performance / Sentry / 自建平台接入,收集帧率、慢路由、崩溃与异常堆栈。自定义打点 :在关键业务流程中插入 Timeline 事件(Timeline.startSync/finishSync),生产环境可选择性开启采样。A/B 对比 :新版本发布时监控帧率与崩溃率回归。采样策略 :用户侧降低采样率(1%-5%)控制性能开销。混合工程:Flutter 指标与 Android 原生指标(Activity 生命周期、内存)接入同一监控看板,做端到端关联分析。
面试官可能继续追问
-
Android APM 工具能收 Flutter 数据吗?
同一套 APM SDK 可收集 Dart 异常与自定义事件;帧率指标需 SDK 支持 Flutter Engine 的帧回调。
-
只监控 Dart 侧够吗?
不够。混合应用须同时监控 Channel 调用耗时与原生侧异常,否则盲区明显。

面试策略速查
| 面试等级 | 建议掌握 |
|---|---|
| 初级 | ★★★☆☆ |
| 中级 | ★★★★☆ |
| 高级 | ★★★★★ |
必背知识(🔥)
60fps/16.6ms 帧预算、UI vs Raster 线程分工、jank 定义与 DevTools 定位、一帧内 build→layout→paint 顺序、列表懒加载与 RepaintBoundary、图片解码尺寸控制、系统化 jank 排查流程、内存泄漏常见根因、Flutter 与 SurfaceFlinger 关系
高频追问
「UI 慢还是 Raster 慢怎么查」「Impeller 解决什么问题」「ListView 优化要点」「Shader jank 原理」「和 Android 渲染链什么关系」「isolate 何时用不用何时」
加分项(💡)
Shader 编译与 SkSL 预热、FFI 与 Channel 互补选型、Flutter Engine 作为 Surface Producer 的角色、生产环境帧率监控
容易踩坑
Debug 模式测性能数据严重失真;盲目加 RepaintBoundary 增加合成成本;大列表使用 ListView(children) 全量构建;shrinkWrap: true 用在大列表导致全量测量;图片未指定 cacheWidth/cacheHeight 按原分辨率解码;UI isolate 执行同步重计算;AnimationController 未 dispose 泄漏;build 中创建非 const 对象破坏复用
完整链路一句通
渲染性能一条线:vsync 触发帧 → UI isolate 执行 build→layout→paint 产出 LayerTree → Raster 线程栅格化(Skia/Impeller)→ GPU 生成 buffer → SurfaceFlinger 合成上屏;超 16.6ms 即 jank → DevTools 分 UI/Raster 定位瓶颈 → 针对性优化列表懒加载、图片尺寸、const 复用、shader 预热、isolate 分流。