chrome 不记录填写值_在 Chrome 中测试 Flutter Web 应用性能

本文介绍如何在Chrome中使用DevTools测试Flutter Web应用的性能,涵盖滚动、页面切换和悬浮按钮点击等场景。通过自动化测试框架记录性能数据,分析帧率、离群值,提供改善应用流畅性的指标。
摘要由CSDN通过智能技术生成

e95e1b52272f58e2fdb0e31d21a84ad1.png

原作博客:https://medium.com/@tianguang 翻译:Meandni

概览

性能测试是开发过程中非常重要的一环,通过合适的工具我们可以发现应用程序卡顿、变慢的潜在原因。本文,我们就来介绍一种在 Chrome 中测试 Flutter Web 应用程序性能的方法,此方法与官方测试 Flutter Gallery 应用性能方法类似。

示例应用

下图展示了我们本文测试的一个示例应用程序,其中包含一个顶部栏,一个悬浮按钮和一个无限列表,列表上方展示按下按钮的次数。

988cd68269fde4af967d815a2aad95c3.png

点击 Appbar 中的 action 图标进入第二个页面,如下:

77455b1690bae2e6d23c517690e22c23.png

应用程序的完成代码如下:

  • https://github.com/material-components/material-components-flutter-experimental/tree/develop/web_benchmarks_example

测试点

我们主要测试该应用在 Chrome 中的以下几种情况:

  1. 用户在无限列表中滚动。
  2. 用户在两个页面之间切换。
  3. 用户点击悬浮操作按钮。

建立框架

在配置文件 pubspec.yaml  中添加如下配置项:

dependencies:
  flutter:
    sdk: flutter
  web_benchmarks_framework:
    git:
      url: https://github.com/material-components/material-components-flutter-experimental.git
      ref: f6ebb4ed3b6489547d9ae58216df9999112be568
      path: web_benchmarks_framework

引入 web_benchmarks_framework ,它是用在 Chrome 中做性能测试最小依赖库。

该库基于 macrobenchmarksdevicelab ,Flutter 官方主要就是使用两个库对 Flutter Gallery 进行了 Web 性能测试。目前,这两个库专用于 flutter/flutter 内部  sample 的 Web 性能测试 ,我们这里使用更加通用的 web_benchmarks_framework

运行 flutter pub get,同步依赖。

编写第一个测试

lib 中新建  benchmarks  文件夹,并创建 runner.dart 文件:

862fbb22be003e836bf5a87818c38c6e.png

在该文件中添加如下代码:

import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:web_benchmarks_framework/recorder.dart';
import 'package:web_benchmarks_framework/driver.dart';
import 'package:web_benchmarks_example/main.dart';
import 'package:web_benchmarks_example/homepage.dart' show textKey;

/// 用于测量框架构建的时间的记录器。
abstract class AppRecorder extends WidgetRecorder {
  AppRecorder({@required this.benchmarkName}) : super(name: benchmarkName);

  final String benchmarkName;

  Future<void> automate();

  @override
  Widget createWidget() {
    Future.delayed(Duration(milliseconds: 400), automate);
    return MyApp();
  }

  Future<void> animationStops() async {
    while (WidgetsBinding.instance.hasScheduledFrame) {
      await Future<void>.delayed(Duration(milliseconds: 200));
    }
  }
}

class ScrollRecorder extends AppRecorder {
  ScrollRecorder() : super(benchmarkName: 'scroll');

  Future<void> automate() async {
    final scrollable = Scrollable.of(find.byKey(textKey).evaluate().single);
    await scrollable.position.animateTo(
      30000,
      curve: Curves.linear,
      duration: Duration(seconds: 20),
    );
  }
}

Future<void> main() async {
  await runBenchmarks(
    {
      'scroll': () => ScrollRecorder(),
    },
  );
}

上面代码包括:

  • 应用运行,创建一个 ScrollRecorder  对象,该对象可以执行自动化手势驱动应用程执行,在上面的代码中,应用启动后就会自动化向下滚动列表。

  • ScrollRecorder 继承 AppRecorder,而 AppRecorder 继承自 WidgetRecorder,其中就会通过驱动应用程序测试记录性能数据。

  • runBenchmarkspackage:web_benchmarks_framework/driver.dart 中实现,该函数允许用户选择一个 benchmark 执行并在浏览器中显示测试结果。

  • automate 方法依赖 flutter_test,可以使用它在应用程序中执行自动化手势和组件 find 等方法。

运行第一个测试

进入项目根目录,在终端运行 flutter run -d chrome -t lib/benchmarks/runner.dart,该命令表示使用 runner.dart 代替 main.dart 作为程序入口点。

a279f3b3615698dc3c252f925ba5da8b.png

目前,我们只有一个 benchmark 测试(ScrollRecorder),因此可以单击这里 “scroll” 直接启动。

8e6d237ab34734c2dde7e79997b3ec1f.png

测试开始后,列表会自动向下滚动,几秒钟后结束,页面内容如下:

3bae44bb161cafc20148d52d630e78de.png

该图表展示了记录时应用绘制每一帧所花费的时间,横轴表示时间线,纵轴表示每帧所花费的具体时间。

图表中前 2/3 背景为灰色,表示这些帧为预热帧(warm-up frames) ,需要从统计信息中省略,预热帧可以给 JIT 编译器一定时间来编译代码,并填充各种缓存,这样,之后所有的测试结果表达就是应用程序的真实性能数据了。但预热帧也不能总被忽略,它也可以在前几秒钟提供有关应用程序性能一些有价值的信息,这些信息也会影响我们对应用程序质量的分析。

红色框中的帧表示离群值(outliers),这些帧相比其他帧更耗时,也很容易被无视,例如,jank 在动画的开始或结束时除非到了特定的点否则将会不可见,但是,动画中间的一个不稳定帧将非常明显。

离群值可以一定程度上说明应用程序的简洁性,通过改进应用,我们可以降低离群值或减少离群数,这时就表明应用已经变得更加流畅了。

从 Chrome 的 DevTools 收集性能数据

该 benchmark 完全在 Chrome 内部运行,创建 test/run_benchmarks.dart ,添加如下代码:

import 'dart:convert' show JsonEncoder;

import 'package:web_benchmarks_framework/server.dart';

Future<void> main () async {
  final taskResult = await runWebBenchmark(
    macrobenchmarksDirectory: '.',
    entryPoint: 'lib/benchmarks/runner.dart',
    useCanvasKit: false,
  );
  print (JsonEncoder.withIndent('  ').convert(taskResult.toJson()));
}

运行 dart test/run_benchmarks.dart。大约一分钟后,看到以下结果:

Received profile data
{
  "success": true,
  "data": {
    "scroll.html.preroll_frame.average": 93.88659793814433,
    "scroll.html.preroll_frame.outlierAverage": 1061.3333333333333,
    "scroll.html.preroll_frame.outlierRatio": 11.304417847077339,
    "scroll.html.preroll_frame.noise": 0.3103013467989926,
    "scroll.html.apply_frame.average": 391.1914893617021,
    "scroll.html.apply_frame.outlierAverage": 1462.3333333333333,
    "scroll.html.apply_frame.outlierRatio": 3.738152217266761,
    "scroll.html.apply_frame.noise": 0.24804233283684318,
    "scroll.html.drawFrameDuration.average": 1496.8690476190477,
    "scroll.html.drawFrameDuration.outlierAverage": 3622.8125,
    "scroll.html.drawFrameDuration.outlierRatio": 2.4202601461781335,
    "scroll.html.drawFrameDuration.noise": 0.38481902033678567,
    "scroll.html.totalUiFrame.average": 3441
  },
  "benchmarkScoreKeys": [
    "scroll.html.drawFrameDuration.average",
    "scroll.html.drawFrameDuration.outlierRatio",
    "scroll.html.totalUiFrame.average"
  ]
}

执行机器不同,这些性能值会有差异。

上面代码主要的内容包括:

  • 运行 test/run_benchmarks.dart 构建 Flutter Web 应用,然后在 Chrome 中运行该应用。
  • test/run_benchmarks.dart 会连接到 Chrome 的 DevTools 端口,并从中监听并收集相关的性能数据。

结果含义如下:

  • 每渲染一帧,layer tree 执行两个步骤。
  • 第一步 “Preroll”,它不渲染任何东西,但是会计算稍后用于渲染的值,例如包括:变换矩阵,逆变换和片段。
  • 第二步是 “Apply frame” ,UI 被实际渲染。
  • “Draw frame” 表示框架渲染一帧所花费的总时间,包括 “Preroll” 和 “Apply frame”,也包括构建和布局组件所花费的时间。
  • “ Total UI frame” 包括 “Draw frame” 中的所有内容,还包括浏览器执行的一些隐藏工作,例如层树更新,样式重新计算和浏览器侧布局(Flutter 自己的布局逻辑不同)。
  • 收集数据集(持续时间列表)后,性能测试算法会省略离群值。
  • 首先,计算数据的平均值和标准差,任何高于(均值+1个标准差)的数据点均被视为离群值。
  • 非离群值(纯数据)的平均值和标准差用于计算数据集的平均值和噪声,然后将其报告。
  • 还报告了所有异常值的平均值,以及“异常值平均值”和“非异常值平均值”的比率。
  • 对于每个数据集,“ outlierRatio” 和 “noise” 都是表明应用程序性能有多少噪声的良好指标。如果结果太嘈杂,则可能表明性能不一致(例如,GC 暂停时出现不稳定的帧),通过降低噪音,可以使应用更流畅地运行。

添加更多测试

修改  lib/benchmarks/runner.dart,添加两个测试。首先,修改 main 函数:

Future<void> main() async {
  await runBenchmarks(
    {
      'scroll': () => ScrollRecorder(),
      'page': () => PageRecorder(),
      'tap': () => TapRecorder(),
    },
  );
}

然后,再添加两个继承 AppRecorder 的类:

class PageRecorder extends AppRecorder {
  PageRecorder() : super(benchmarkName: 'page');

  bool _completed = false;

  @override
  bool shouldContinue() => profile.shouldContinue() || !_completed;

  Future<void> automate() async {
    final controller = LiveWidgetController(WidgetsBinding.instance);
    for (int i = 0; i 10; ++i) {
      print('Testing round $i...');
      await controller.tap(find.byKey(aboutPageKey));
      await animationStops();
      await controller.tap(find.byKey(backKey));
      await animationStops();
    }
    _completed = true;
  }
}

class TapRecorder extends AppRecorder {
  TapRecorder() : super(benchmarkName: 'tap');

  bool _completed = false;

  @override
  bool shouldContinue() => profile.shouldContinue() || !_completed;

  Future<void> automate() async {
    final controller = LiveWidgetController(WidgetsBinding.instance);
    for (int i = 0; i 10; ++i) {
      print('Testing round $i...');
      await controller.tap(find.byIcon(Icons.add));
      await animationStops();
    }
    _completed = true;
  }
}

这里的内容包括:

  • 这里添加了剩余的两个 benchmark 测试:一个用于在页面之间切换(PageRecorder),另一个用于点击悬浮操作按钮(TapRecorder)。
  • animationStops 会一直检查动画是否正在发生,所有动画停止后才停止,这就可以确保成功过渡到打开的第二个页面。
  • 在 “page” 和 “tap” benchmarks 中,_completed 表示自动手势是否完成。
  • 在 “page” 和 “tap” benchmarks中,重写 shouldContinue  方法可以实现所有手势完成后 AppRecorder停止记录帧。

运行测试

要在 Chrome 中运行这些测试(并查看动画),执行下面这行命令:

flutter run -d chrome -t lib/benchmarks/runner.dart --profile

要运行这些测试并收集 DevTools 数据,执行下面这行命令:

dart test/run_benchmarks.dart

下一步

一旦用这种方式收集到了性能数据,就可以根据需要使用啦:

  • 可以在 CI 中设置一个任务,每当有人提交 PR 时就运行这些 benchmark 测试,避免引入高性能消耗的 change。
  • 也可以设置一个 dashboard 页面,来分析性能测试 benchmark 的趋势,如官方为  Flutter Gallery 做的 Flutter Dashboard

相关资源链接可点击「阅读全文」查看我的博客原文。也欢迎大家和我一起交流。

3c39cad6b06d38888cd207cdddd045aa.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值