如果我们需要对比Flutter与Native的性能数据,那么我们就需要获取Flutter的一部分性能数据,FPS就是其中的一个衡量标准。
至于Flutter的FPS的计算方式,可以参考这篇文章,里面讲的比较细以及为什么这么计算。
总体思路就是设置window.onReportTimings回调,获取每帧的数据,但这里需要注意一下,v1.9.1可以通过设置window.onReportTimings实现,如
1 | var orginalCallback = window.onReportTimings; |
不过在v1.12.13上,你不能再使用这方式,你得改成如下方式
1 | import 'package:flutter/scheduler.dart'; |
设置完回调后过滤掉无效的帧数据后根据计算公式获得对应的FPS。对应的公式如下:
FPS / 60 ≈ drawFramesCount / (drawFramesCount + droppedCount)
根据公式推导出:
FPS ≈ 60 * drawFramesCount / (drawFramesCount + droppedCount)
假设过滤后的有效的帧数据为变量framesSet,那么
- drawFramesCount则是我们绘制的帧数,这个可以直接通过framesSet.length获取
- droppedCount 则是丢帧数,可以通过判断绘制的时间是否大于每帧绘制时间获取
- 每帧绘制的时间可以用1000ms/刷新频率获取,即1000/60 ≈ 16ms
假如我们将drawFramesCount + droppedCount赋值为costCount,那么 FPS 就可以通过如下代码获取得到:
1 | const frameInterval = const Duration(microseconds: Duration.microsecondsPerSecond ~/ 60); |
这里看起来很完美,其实有个致命的问题,那就是真的每个手机的每秒绘制的最大帧数是60帧吗,也就是16ms绘制一帧,显然不是的。可以参考下如下文章
可以看到,部分手机肯定不是60Hz的刷新频率,可能存在90Hz,如OnePlus 7 Pro,甚至出现了120Hz,如Redmi K30,而Flutter的目标就是在60Hz的手机上达到每秒60帧的绘制,在120Hz的手机上达到每秒120帧的绘制。如果我们上述公式写死用60进行计算,那么在这些手机上计算出来的FPS就会偏小。
所以为了让上述计算公式更准确,我们需要获取到手机屏幕的刷新频率。
通过查看Flutter Engine的源码,我们会发现Vsync的刷新频率是通过Platform的API获取到的
首先来看下Android是如何获取的
engine/src/flutter/shell/platform/android/vsync_waiter_android.cc
1 | float VsyncWaiterAndroid::GetDisplayRefreshRate() const { |
通过JNI调用Java类FlutterJNI的一个静态字段refreshRateFPS获取,而该字段是在VsyncWaiter类中获取到的,其代码路径为
engine/src/flutter/shell/platform/android/io/flutter/view/VsyncWaiter.java
1 | public class VsyncWaiter { |
从代码看出Android的屏幕刷新频率可以通过WindowManager的API获取到
1 | public double getRefreshRate(Context context) { |
我们再来看看iOS如何获取到屏幕刷新频率,其关键代码位于
engine/src/flutter/shell/platform/darwin/ios/framework/Source/vsync_waiter_ios.mm
1 | - (float)displayRefreshRate { |
也就是说可以通过CADisplayLink的displayLinkWithTarget函数获取到,如下
1 | - (double)displayRefreshRate:(CADisplayLink *)link { |
最后,通过channel让dart层可以调用如上函数获取到对应的屏幕刷新频率即可
fps.dart
1 | static const MethodChannel _channel = MethodChannel('fps_plugin'); |
对应Android和iOS实现channel,调用上面的函数获取refreshRate即可
最终FPS的计算公式就会变成
1 | double _refreshRate; |
改进后的方法将会在屏幕刷新频率为90Hz和120Hz的手机上更加准确。