写点什么

Flutter 性能优化 Tips,Android 攒了一个月的面试题及解答

用户头像
Android架构
关注
发布于: 13 小时前
  1. 因为 debug 模式会有一些额外的检查工作,比如assert()

  2. 为了加速开发效率,debug 模式是以 JIT(Just in time)模式编译 dart 代码的,而 profile 和 release 是提前编译为机器码 AOT(Ahead Of Time),所以 debug 会慢很多


  1. 在 Android Studio and IntelliJ 中, 在菜单栏中点击 Run > Flutter Run main.dart in Profile Mode

  2. VS Code:打开 launch.json 文件并设置 flutterMode 为 profile:


"configurations": [{"name": "Flutter","request": "launch","type": "dart","flutterMode": "profile" # 测试完后记得把它改回去!}]


  1. 用命令行启动:


$ flutter run --profile

检测帧率

那么检测帧率有哪些方法呢?Flutter 给我们提供了 Performance Overlay,如下图,绿色代表当前渲染帧。



我们有三种开启方式


  1. 在 Android Studio 和 IntelliJ IDEA 中: 选中 View > Tool Windows > Flutter Inspector. 点击下面这个按钮。



  1. 在 VS Code 中 选中 View > Command Palette… 会显示一个 command 面板. 在命令面板中输入 performance 并选择 Toggle Performance Overlay 如果命令显示为不可用,需要检查 app 是否正在运行.

  2. 从命令行中运行 键盘输入P

  3. 代码中打开 在MaterialApp 或者 WidgetsApp的构造函数中设置showPerformanceOverlay 属性为 true :


class MyApp extends StatelessWidget {@overrideWidget build(BuildContext context) {return MaterialApp(showPerformanceOverlay: true, // 开启 title: 'My Awesome App',home: MyHomePage(title: 'My Awesome App'),);}}


然后就是动手操作 app,并观察图表上是否出现红色线条。绿色代表当前帧,当页面有变动,图表会不断绘制。蒙版上有 2 个图表,每个图表上有三横格,每个横格代表 16ms。如果大多数帧都在第一格,说明达到了期望的帧率。



图表分别体现了 UI 帧率 和 GPU 帧率。如果出现了红色,说明对应的线程有太多 work 要做。那先来了解一下 Flutter 中的 4 个主要线程分别承担了什么职责。


  • Platform 线程:插件代码运行的线程;即 Android/iOS 的主线程,

  • UI 线程:在 Dart 虚拟机中执行 Dart 代码。作用是创建视图树,然后将它发送给 GPU。注意不要阻塞此线程!

  • GPU 线程:把上面提到的视图树渲染出来,虽然我们在 flutter 中不能直接访问 GPU 线程和数据,但是 Dart 代码可能导致此线程变慢

  • I/O 线程:执行比较耗时的任务


在运行 app 的过程中,观察爆红的地方和触发场景,进行分析。

分析思路

  • 如果是 UI 报红:那么可能是执行了某个较耗时的函数?或者函数调用过多?算法复杂度高?

  • 如果只是 GPU 报红:那么可能是要绘制的图形过于复杂?或者执行了过多 GPU 操作?

  • 比如要实现一个混合图层的半透明效果:如果把透明度设置在顶层控件上,CPU 会把每个子控件图层渲染出来,再执行saveLayer操作保存为一个图层,最后给这个图层设置透明度。而saveLayer开销


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


很大,这里官方给出了一个建议:首先确认这些效果是否真的有必要;如果有必要,我们可以把透明度设置到每个子控件上,而不是父控件。裁剪操作也是类似。


  • 还有一个拖慢 GPU 渲染速度的是没有给静态图像做缓存,导致每次 build 都会重新绘制。我们可以把静态图形加到RepaintBoundry控件中,引擎会自动判断图像是否复杂到需要用 repaint boundary,不需要的话也会忽略。

  • 开启 saveLayer 和图形缓存的检查


MaterialApp(showPerformanceOverlay: true,checkerboardOffscreenLayers: true, // 使用了 saveLayer 的图形会显示为棋盘格式并随着页面刷新而闪烁 checkerboardRasterCacheImages: true, // 做了缓存的静态图片在刷新页面时不会改变棋盘格的颜色;如果棋盘格颜色变了说明被重新缓存了,这是我们要避免的...);

提高流畅性的策略

  • 代码调用时机是否可以延后?如底部导航栏式的页面,没有必要第一次进入就把每个子 Page 都创建出来

  • 尽量做到局部刷新

  • 把耗时的计算放到独立的 isolate 去执行

  • 检查不必要的 saveLayer

  • 检查静态图片是否添加缓存

  • relayout boundary:[参考](


)


  • repaint boundary:[参考](


)

内存优化

在内存优化方面,我们的目标是希望减少应用内存占用,减少被系统杀死的概率,同时尽可能的避免内存泄露,减少内存碎片化。

内存优化策略

  • 加载对象过大?如图片质量和尺寸不做限制就加载

  • 加载对象过多?如加载长列表;在调用频率很高的方法中创建对象

  • 合理设置缓存大小/长度

  • 在内存不足时或离开页面时清空缓存数据

  • 使用 ListView.build()来复用子控件

  • 自定义绘图中避免在 onDraw 中做创建对象操作,或者相同的参数设置

  • 复用系统提供的资源,比如字符串、图片、动画、样式、颜色、简单布局,在应用中直接引用

  • 内存泄露的问题?比如 dispose 需要销毁的 listener 等

  • 不可见的视图是否也在 build?

  • 页面离开后的网络请求是否取消?

如何获取内存状态

Dart 提供了一个性能检测工具 Observatory,我在最后一部分会进行详细介绍

优化证明

优化证明的意义

性能优化不像其它的开发需求只要完成功能即可,它需要通过统计和数据来证明优化的效果。比如帧率有了多少提高?CPU 占用率降低了多少?内存占用减少了多少?对比其它优化策略,哪个优化效果好?

优化证明的流程

举个例子

以检查流畅性为例

1. 在 profile 模式下运行并开启 Performance Overlay,整体测试 app

2. 找到帧率报红色的模块

3. 把页面孤立出来,并多次测量,并得到 baseline(参照)帧率数据。比如长列表页面出现了卡顿,我们可以用 TestDriver 写一个 ListView 滑动的性能测试(更多参考[Flutter gallery](

))


scroll_pref.dart


void main() {enableFlutterDriverExtension();runApp(const GalleryApp(testMode: true));}


scroll_perf_test.dart


void main() {group('scrolling performance test', () {FlutterDriver driver;


setUpAll(() async {driver = await FlutterDriver.connect();});


tearDownAll(() async {if (driver != null)driver.close();});


test('measure', () async {final Timeline timeline = await driver.traceAction(() async {await driver.tap(find.text('Material'));


final SerializableFinder demoList = find.byValueKey('GalleryDemoList');


for (int i = 0; i < 5; i++) {await driver.scroll(demoList, 0.0, -300.0, const Duration(milliseconds: 300));await Future<void>.delayed(const Duration(milliseconds: 500));}


// Scroll upfor (int i = 0; i < 5; i++) {await driver.scroll(demoList, 0.0, 300.0, const Duration(milliseconds: 300));await Future<void>.delayed(const Duration(milliseconds: 500));}});


TimelineSummary.summarize(timeline)..writeSummaryToFile('home_scroll_perf', pretty: true)..writeTimelineToFile('home_scroll_perf', pretty: true);});});}


在命令行下执行以下命令


flutter driver --target=test_driver/scroll_perf.dart


这个命令会:


  • build 目标 app,并把它安装到设备上

  • 运行位于test_driver/目录下的scroll_perf_test.dart的测试( flutter drive 能帮你找到带 _test后缀的同名文件)


Test Driver 将会安装 app 到设备上,再跳转到 Material-GalleryDemoList 页面,做 5 次滑动列表的操作。执行完成后会借助 TimelineSummary ,在 build 目录下生成两个 json 文件:home_scroll_perf.timeline.jsonhome_scroll_perf.timeline_summary.json。这里我们看一下timeline_summary.json文件的内容


{"average_frame_build_time_millis": 5.6319655172413805, # 平均每帧 build 时间"90th_percentile_frame_build_time_millis": 10.216,"99th_percentile_frame_build_time_millis": 17.168,"worst_frame_build_time_millis": 20.415, # 最长帧 build 时间"missed_frame_build_budget_count": 21, # build 期丢帧数"average_frame_rasterizer_time_millis": 14.234294964028772, # 平均每帧光栅化时间"90th_percentile_frame_rasterizer_time_millis": 22.338,"99th_percentile_frame_rasterizer_time_millis": 42.661,"worst_frame_rasterizer_time_millis": 43.161,"missed_frame_rasterizer_budget_count": 112,"frame_count": 116,"frame_build_times": [

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Flutter 性能优化 Tips,Android攒了一个月的面试题及解答