前言
我们在开发iOS或者安卓中都有一个概念,在开发时都会调出FPS在界面上显示,flutter也不例外。
一、代码
import 'dart:collection';
import 'dart:ui';
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
/**
* WidgetsFlutterBinding就是一个胶水类,用于初始化其他with的minxin类
* minxin就是一个普通类,用了解决多继承问题,配合with使用解决多继承
*/
WidgetsFlutterBinding.ensureInitialized().addTimingsCallback(_onReportTimings);
// window.onReportTimings = _onReportTimings;
/**
* 不能直接window.onReportTimings,否则会报错
* package:flutter/src/scheduler/binding.dart': Failed assertion: line 232 pos 12:
I/flutter ( 569): 'window.onReportTimings == _executeTimingsCallbacks': is not true
*/
}
const maxframes = 100; // 100 帧足够了,对于 60 fps 来说
const _frameInterval = const Duration(microseconds: Duration.microsecondsPerSecond ~/ 60);
final lastFrames = ListQueue<FrameTiming>(maxframes);
void _onReportTimings(List<FrameTiming> timings) {
//把 Queue 当作堆栈用
for (FrameTiming timing in timings) {
//时间从小到大,也就表示老贞在前面小索引位置,新贞在后面的大索引位置
print(timing.timestampInMicroseconds(FramePhase.buildStart));
// 时间大的(新贞)被安排在队列头部, 时间小的(老贞)被安排在队列尾部
lastFrames.addFirst(timing);
}
// 只保留 maxframes
while (lastFrames.length > maxframes) {
//移除队列尾部的老贞
lastFrames.removeLast();
}
print('fps=======:' + fps.toString());
}
double get fps {
var lastFramesSet = <FrameTiming>[];
for (FrameTiming timing in lastFrames) {
if (lastFramesSet.isEmpty) {
lastFramesSet.add(timing);
} else {
var lastStart = lastFramesSet.last.timestampInMicroseconds(FramePhase.buildStart);
// 相邻两帧如果开始结束相差时间过大,比如大于 frameInterval * 2,是不同绘制时间段产生的,放在一起肯定会拉低fps
// 因为相当于有一段时间fps=0,根本没有产生绘制
if (lastStart - timing.timestampInMicroseconds(FramePhase.rasterFinish) >
(_frameInterval.inMicroseconds * 2)) {
// in different set
print('break...');
break;
}
print('add...');
lastFramesSet.add(timing);
}
}
var framesCount = lastFramesSet.length;
var costCount = lastFramesSet.map((t) {
// 耗时超过 frameInterval 会导致丢帧
// 下面这个公式算出来的是实际上应该发生的理论总贞数,结合图理解
return (t.totalSpan.inMicroseconds ~/ _frameInterval.inMicroseconds) + 1;
}).fold(0, (a, b) => a + b);
return framesCount * 60 / costCount;
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
int frameInterval =
const Duration(microseconds: Duration.microsecondsPerSecond ~/ 60).inMilliseconds;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: TextStyle(color: Colors.red),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
二、出现问题
可能因为flutter版本的原因,这一段代码会报错:
var costCount = lastFramesSet.map((t) {
// 耗时超过 frameInterval 会导致丢帧
// 下面这个公式算出来的是实际上应该发生的理论总贞数,结合图理解
return (t.totalSpan.inMicroseconds ~/ _frameInterval.inMicroseconds) + 1;
}).fold(0, (a, b) => a + b);
报错原因:
The operator '+' can't be unconditionally invoked because the receiver can be 'null'. (Documentation) Try adding a null check to the target ('!')
三、解决方法
改为这样即可:
var costCount = lastFramesSet.map((t) {
// 耗时超过 frameInterval 会导致丢帧
// 下面这个公式算出来的是实际上应该发生的理论总贞数,结合图理解
return (t.totalSpan.inMicroseconds ~/ _frameInterval.inMicroseconds) + 1;
}).fold(0, (a, b) => (a as int) + b);
参考地址:
Flutter 性能计算之流畅性fps计算
Flutter 如何更加准确地获取 FPS
Flutter FPS优化一期技术点总结