简介
性能测试用于评估应用的性能表现。本文介绍如何用 Timeline 采集启动耗时、滚动帧率等指标,并生成性能报告。
版本提示:下文使用
flutter_driver(test_driver/+flutter drive工作流)。该包已被官方废弃,新项目请使用integration_test(integration_test/目录 +flutter test integration_test);flutter_driver的 API 在旧版本 SDK 上仍可运行,TimelineSummary等概念与新版相通。
基本配置
添加依赖
dev_dependencies:
flutter_test:
sdk: flutter
flutter_driver:
sdk: flutter
test: ^1.24.1
创建测试文件
// test_driver/app.dart
import 'package:flutter_driver/driver_extension.dart';
import 'package:my_app/main.dart' as app;
void main() {
enableFlutterDriverExtension();
app.main();
}
// test_driver/app_test.dart
import 'package:flutter_driver/flutter_driver.dart';
import 'package:test/test.dart';
void main() {
group('Performance Tests', () {
late FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect();
});
tearDownAll(() async {
await driver.close();
});
// 测试用例
});
}
测试方法
1. 启动时间测试
test('Measure app startup time', () async {
final timeline = await driver.traceAction(() async {
await driver.waitUntilFirstFrameRasterized();
});
final summary = TimelineSummary.summarize(timeline);
print('Startup time: ${summary.frameBuildTime}ms');
});
2. 滚动性能测试
test('Measure scroll performance', () async {
// 定位滚动列表
final listFinder = find.byType('ListView');
final timeline = await driver.traceAction(() async {
// 执行滚动操作
await driver.scroll(
listFinder,
0,
-300,
Duration(milliseconds: 500),
);
});
final summary = TimelineSummary.summarize(timeline);
print('Average frame build time: ${summary.averageFrameBuildTime}ms');
print('Worst frame build time: ${summary.worstFrameBuildTime}ms');
print('Frame count: ${summary.frameCount}');
});
完整示例
void main() {
group('Performance Tests', () {
late FlutterDriver driver;
setUpAll(() async {
driver = await FlutterDriver.connect();
// 等待应用稳定
await driver.waitUntilFirstFrameRasterized();
});
tearDownAll(() async {
await driver.close();
});
test('Measure app startup performance', () async {
final timeline = await driver.traceAction(() async {
await driver.waitFor(find.text('Welcome'));
});
final summary = TimelineSummary.summarize(timeline);
// 输出性能数据
print('===== App Startup Performance =====');
print('Frame build time: ${summary.frameBuildTime}ms');
print('Frame rasterizer time: ${summary.frameRasterizerTime}ms');
// 保存跟踪文件
await summary.writeTimelineToFile(
'startup_timeline',
pretty: true,
);
});
test('Measure scrolling performance', () async {
// 等待列表加载
await driver.waitFor(find.byType('ListView'));
final timeline = await driver.traceAction(() async {
// 执行多次滚动
for (var i = 0; i < 5; i++) {
await driver.scroll(
find.byType('ListView'),
0,
-300,
Duration(milliseconds: 500),
);
await Future.delayed(Duration(milliseconds: 500));
}
});
final summary = TimelineSummary.summarize(timeline);
print('===== Scrolling Performance =====');
print('Average frame build time: ${summary.averageFrameBuildTime}ms');
print('90th percentile frame build time: '
'${summary.computePercentileFrameBuildTime(90.0)}ms');
print('Worst frame build time: ${summary.worstFrameBuildTime}ms');
print('Frame count: ${summary.frameCount}');
print('Frame build time histogram: ${summary.buildTimeHistogram}');
// 保存跟踪文件
await summary.writeTimelineToFile(
'scrolling_timeline',
pretty: true,
);
});
test('Measure memory usage', () async {
final memory = await driver.getVMStatistics();
print('===== Memory Usage =====');
print('Used heap size: ${memory['heapUsed']} bytes');
print('Total heap size: ${memory['heapTotal']} bytes');
print('External memory: ${memory['external']} bytes');
});
});
}
性能指标
1. 时间指标
- 启动时间
- 页面加载时间
- 动画帧时间
- 响应时间
2. 内存指标
- 内存使用量
- 内存泄漏
- GC 频率
3. CPU 指标
- CPU 使用率
- 热点函数
- 线程使用情况
性能分析工具
1. DevTools
// 启用 DevTools 性能叠加层
void main() {
if (kDebugMode) {
debugPaintSizeEnabled = true;
debugPrintMarkNeedsLayoutStacks = true;
debugPrintMarkNeedsPaintStacks = true;
}
runApp(MyApp());
}
2. Performance Overlay
MaterialApp(
showPerformanceOverlay: true,
home: MyHomePage(),
)
最佳实践
- 先定基线再优化:同一台真机、同样电量和发热状态下跑 3 次取中位数作为基线。不同手机的帧率基线完全不同(实战案例里说的),拿千元机的数字跟旗舰机的比没有意义。
- 只盯 90 分位和最差帧:平均帧构建时间会被大量空闲帧拉低——列表滚动时 1 帧 200ms 的卡顿在平均值里根本看不见。
computePercentileFrameBuildTime(90.0)和 worstFrameBuildTime 才是用户体感。 traceAction的范围尽量小:只包住”滚动 500ms”这个动作(正文示例),别把”等页面加载+滚动+返回”全包进一个 timeline——混在一起出问题定位不到是哪一段。- 用 release 模式测性能:debug 模式的帧时间没有参考价值(JIT、断言全在拖慢)。
flutter drive --release或 integration_test 打 release 包——这是正文示例没写但必须做的一步。 - 性能测试进 CI 但别卡门:帧率数字抖动大(实战案例),直接设硬阈值会天天误报。做法:CI 只记录趋势(存 timeline 文件),人工 review 跌幅;等基线稳定了再谈卡点。
注意事项
- 测试环境要固定:关掉后台应用、同一台设备、最好插电。手机一发热降频,帧时间直接翻倍——你以为是代码退化,其实是手机烫了。
flutter_driver已废弃:正文用的flutter_driver在新 SDK 上跑不起来,新项目用integration_test(开头版本提示已写)。TimelineSummary的概念相通,API 照着官方文档迁移。- 别在模拟器上测性能:x86 模拟器的 GPU 是宿主机混渲,帧时间跟真机完全两个世界。模拟器只验证”能跑”,性能数字只认真机。
- 采样别太贪:
writeTimelineToFile(pretty: true)的 timeline 文件一次滚动就能几十 MB。CI artifact 只保留 summary 的几个关键数字,原始文件按需下载。 - 内存测试看趋势不看绝对值:
getVMStatistics的 heapUsed 一次快照说明不了泄漏——要的是”重复进出页面 20 次后 heap 是否持续上涨”。单次数字正常、趋势上涨才是泄漏。
实战案例
性能测试(自动化跑帧率、内存基线)我还没在项目里做过。我的性能工作全是手动的:DevTools Performance 视图看 raster/UI 线程耗时,flutter_travel 列表卡顿时定位到图片解码,一次解决。自动化性能测试的门槛在于基线管理——不同手机的帧率基线完全不同,CI 跑出来的数字抖动很大,误报比漏报还烦人。小团队我的建议:先把 flutter test 的 widget 测试跑起来(电商奢侈品专区改版 就是这么做的),性能靠 DevTools + 真机走查。等团队有专人看质量了,再考虑集成 integration_test 的性能采集。参考:Flutter 官方文档性能测试章节。
总结
性能测试是保证应用性能的重要手段。通过合理的性能测试和监控,可以及时发现和解决性能问题,提供更好的用户体验。
