IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Flutter 性能优化 Tips(1),2021年Android开发实战 -> 正文阅读

[移动开发]Flutter 性能优化 Tips(1),2021年Android开发实战

  • Platform线程:插件代码运行的线程;即Android/iOS的主线程,
  • UI线程:在Dart虚拟机中执行Dart代码。作用是创建视图树,然后将它发送给GPU。注意不要阻塞此线程!
  • GPU线程:把上面提到的视图树渲染出来,虽然我们在flutter中不能直接访问GPU线程和数据,但是Dart代码可能导致此线程变慢
  • I/O线程:执行比较耗时的任务

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

分析思路

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

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

    • 比如要实现一个混合图层的半透明效果:如果把透明度设置在顶层控件上,CPU会把每个子控件图层渲染出来,再执行saveLayer操作保存为一个图层,最后给这个图层设置透明度。而saveLayer开销很大,这里官方给出了一个建议:首先确认这些效果是否真的有必要;如果有必要,我们可以把透明度设置到每个子控件上,而不是父控件。裁剪操作也是类似。
    • 还有一个拖慢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占用率降低了多少?内存占用减少了多少?对比其它优化策略,哪个优化效果好?

优化证明的流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lXtaWZV1-1630843885559)(https://user-gold-cdn.xitu.io/2018/12/13/167a744056e6fa16?imageView2/0/w/1280/h/960/ignore-error/1)]

举个例子

以检查流畅性为例

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 up
        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));
        }
      });

      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": [
      ... 
  ],# 所有帧的 build 时间
  "frame_rasterizer_times": [
      ...
  ] # 所有帧的光栅化时间
} 

4. 优化

5. 用步骤3的方法再次测量,对比baseline得出确切的优化效果

Flutter 提供的性能调试 API

更多可以参考官方文档

性能检测利器 Observatory

Observatory 是用于分析和调试Dart应用程序的工具。Observatory允许您根据需要查看正在运行的Dart虚拟机(VM),并提供实时,即时的数据报告。您可以使用它来浏览应用程序的很多状态。

打开Observatory

有2种方式:

  1. 在 androidStudio 中打开Flutter Inspector面板,点击小闹钟图标,如下图

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CjfZ4PrZ-1630843885561)(https://user-gold-cdn.xitu.io/2018/12/13/167a744054e65951?imageView2/0/w/1280/h/960/ignore-error/1)]

  2. 再命令行中运行flutter run,应用启动成功后,命令行中会输出一个 url,把 url copy 到浏览器即可。

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XpObq9ej-1630843885562)(https://user-gold-cdn.xitu.io/2018/12/13/167a7440543d785b?imageView2/0/w/1280/h/960/ignore-error/1)]

打开Observatory面板,要先选择isolate,表示当前应用。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ztoWFkNc-1630843885564)(https://user-gold-cdn.xitu.io/2018/12/13/167a7440b66f043c?imageView2/0/w/1280/h/960/ignore-error/1)]

主要页面

下面是性能优化常关注的几个页面。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZAvFoBys-1630843885565)(https://user-gold-cdn.xitu.io/2018/12/13/167a74409e0c1cb7?imageView2/0/w/1280/h/960/ignore-error/1)]

1. CPU Profile

app的时间都花在哪了?

进入这个页面后要一般需加载个几秒钟,so be patient。图表的下部按cpu占用比例做了一个列表,反映的是函数的调用次数和执行时间(划重点)。一般排在前面的函数(这些函数是?有待学习)都不是我们写的dart代码。如果你发现自己的某个函数调用占比反常,那么可能存在问题。

注:flutter程序的cpu profile和官方文档上的数据展示不太一样,没有VM tags,所以对于百分比的具体含义有待研究。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WruPs9Ls-1630843885566)(https://user-gold-cdn.xitu.io/2018/12/13/167a7440d4531838?imageView2/0/w/1280/h/960/ignore-error/1)]

采样过程:它每隔一定时间对isolate做采样,采样的数据存储在一个环形缓冲区(叫做profile),它能存放约2分钟的数据,一旦缓冲区满了,它会用最新的sample替换掉最旧的。

  • Profile contains:采样时长和对应的采样数
  • Sampling:采样频率,默认1000Hz,即每毫秒采样一次

2. Allocation Profile

内存都被谁吃了?

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dBFM812A-1630843885567)(https://user-gold-cdn.xitu.io/2018/12/13/167a7440e7aade09?imageView2/0/w/1280/h/960/ignore-error/1)]

Heap 堆,动态分配的Dart对象所在的内存空间
,一旦缓冲区满了,它会用最新的sample替换掉最旧的。

  • Profile contains:采样时长和对应的采样数
  • Sampling:采样频率,默认1000Hz,即每毫秒采样一次

2. Allocation Profile

内存都被谁吃了?

[外链图片转存中…(img-dBFM812A-1630843885567)]

Heap 堆,动态分配的Dart对象所在的内存空间

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2021-09-06 11:16:16  更:2021-09-06 11:16:41 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 16:34:36-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码