文章目录
- 学习建议
- 基础层(7 题)
- #1 Flutter 如何保持 60fps? 🔥
- 标准回答
- 面试官可能继续追问
- #2 UI 线程和 Raster 线程分工? 🔥
- 标准回答
- 面试官可能继续追问
- #3 什么是 jank?如何感知? 🔥
- 标准回答
- 面试官可能继续追问
- #4 `SchedulerBinding` 与帧回调? ⭐
- 标准回答
- 面试官可能继续追问
- #5 Skia 在 Flutter 中的作用? ⭐
- 标准回答
- 面试官可能继续追问
- #6 Impeller 是什么? ⭐
- 标准回答
- 面试官可能继续追问
- #7 DevTools 性能页看什么? 🔥
- 标准回答
- 面试官可能继续追问
- 进阶层(7 题)
- #8 一帧内 build/layout/paint 顺序? 🔥
- 标准回答
- 面试官可能继续追问
- #9 Layer 与合成怎么做? ⭐
- 标准回答
- 面试官可能继续追问
- #10 列表性能如何优化? 🔥
- 标准回答
- 面试官可能继续追问
- #11 图片与内存优化? 🔥
- 标准回答
- 面试官可能继续追问
- #12 `const` 与重建优化? ⭐
- 标准回答
- 面试官可能继续追问
- #13 isolate 何时用? ⭐
- 标准回答
- 面试官可能继续追问
- #14 首屏与启动优化? ⭐
- 标准回答
- 面试官可能继续追问
- #15 Shader 编译 jank 是什么? 💡
- 标准回答
- 面试官可能继续追问
- 核心层(6 题)
- #16 如何系统排查一次 jank? 🔥
- 标准回答
- 面试官可能继续追问
- #17 Flutter 渲染与 Android Display Pipeline 关系? 💡
- 标准回答
- 面试官可能继续追问
- #18 `PerformanceOverlay` 看什么? ⭐
- 标准回答
- 面试官可能继续追问
- #19 内存泄漏常见原因? 🔥
- 标准回答
- 面试官可能继续追问
- #20 包体积与运行时性能? ⭐
- 标准回答
- 面试官可能继续追问
- #21 FFI 与性能? 💡
- 标准回答
- 面试官可能继续追问
- #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):执行 Widgetbuild、RenderObjectlayout、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 信号到来前完成全部渲染工作,导致画面跳过该帧,用户感知为卡顿、跳动或动画不流畅。排查手段:DevToolsPerformance页面查看帧时间条,超出红线即为 jank 帧;debugPrintRebuildDirtyWidgets可辅助定位频繁重建的 Widget。Android 侧可用 Systrace/Perfetto 对齐观察。开发调试时开启Repaint Rainbow可直观看到重绘区域,辅助判断重绘范围是否过大。
面试官可能继续追问
Android Systrace 能看 Flutter 帧吗?
可以。同时开启 DevTools Timeline 与 Systrace,对齐 UI/Raster 时间段可做联合分析。偶发 jank 和持续 jank 原因有何不同?
偶发 jank 常见于 GC 触发、首帧初始化、Shader 编译;持续 jank 通常是 build 范围过大或列表未懒加载等系统性问题。
#4SchedulerBinding与帧回调? ⭐
标准回答
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当前占用;排查是否有原图直接显示或缓存未释放。
#12const与重建优化? ⭐
标准回答
const构造函数使 Widget 实例在编译期确定,运行时同一constWidget 始终是同一个实例,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 体系但合成点相同。
#18PerformanceOverlay看什么? ⭐
标准回答
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 分流。
相关推荐
Android 高级工程师面试:Flutter Widget 体系 近1年高频追问 20 题
Android 高级工程师面试:Flutter 布局与绘制 近1年高频追问 20 题