目的
介紹Flutter的性能檢測工具Observator、DevTools的如何使用,同時提供了一個Demo測試用例來檢測APP性能的提升情況
工具使用
不管是vsCode還是Android Studio都提供了觀測臺的功能。
-
Observator
打開的方式一般都是在terminal中輸入flutter run,如果要使用真機(jī)測試則輸入flutter run --profile。成功后會出現(xiàn)如圖所示的網(wǎng)址,打開該網(wǎng)址
-
DevTools
輸入命令行flutter run --profile將程序跑起來后,打開DevTools會出現(xiàn)如圖所示的網(wǎng)頁,不要用Safari及其他瀏覽器中打開,因為DevTools只支持用Chrome瀏覽器打開。
一般我們使用的都是Observatory的timeline部分。
一般在timeline中,我們一般選用Flutter Developer的選項。出現(xiàn)的渲染顯示我們一般會看到gpu和ui的渲染,以及重構(gòu)過程。
性能優(yōu)化
在性能優(yōu)化之前,我們需要知道Flutter重構(gòu)的邏輯。
在Android中我們知道繪制需要的三個步驟是measure、layout、draw,
在iOS中UIView繪制可參考iOS-UI繪制原理
而Flutter對應(yīng)的是build、layout、paint。
Flutter的重構(gòu)是基于一種標(biāo)臟和重新創(chuàng)建的方式進(jìn)行的,所以我們的性能影響一般來自于一個復(fù)雜界面的不斷重建??赡苣阒恍枰薷囊粋€很小的部分,也就是很小的一個子樹需要進(jìn)行修改,那么在代碼沒有規(guī)范的情況下,可能會出現(xiàn)整個界面的刷新,這樣我們的性能可能就要下降了數(shù)倍。
對于我的代碼而言,就是整個界面的代碼都得到了重建的,但是這是基于本身代碼還是簡單的原因,如果代碼是非常復(fù)雜的,對應(yīng)用的性能就會產(chǎn)生極大的影響。

上文的意思用這張圖來表示,本來我們只想重構(gòu)的是畫綠叉的子樹,但是由于代碼書寫的原因,導(dǎo)致的結(jié)果是重構(gòu)了畫紅叉的整棵樹。所以代碼的書寫規(guī)范在性能優(yōu)化上起了至關(guān)重要的作用。
代碼測試
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
class Test extends StatefulWidget {
@override
State createState() => _Test();
}
class _Test extends State<Test> {
// int _num = 0;
// @override
// void initState() {
// // TODO: implement initState
// super.initState();
// Timer.periodic(Duration(seconds: 1), (timer) {
// setState(() {
// _num = timer.tick;
// });
// });
// }
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("test"),
),
body: Column(
children: [
_buildTop(),
_buildMiddle(),
// Bottom
// Flex(
// direction: Axis.horizontal,
// children: [
// Expanded(
// flex: 1,
// child: Card(
// color: Colors.black26,
// child: Container(
// padding: EdgeInsets.fromLTRB(0, 100, 0, 100),
// child: Center(
// child: Text("$_num"),
// ),
// ),
// )),
// ],
// ),
BuildBottom()
],
),
);
}
Widget _buildMiddle() {
return SizedBox(
height: 150,
child: ListView.builder(
itemCount: 3,
scrollDirection: Axis.horizontal,
itemBuilder: (context, index) {
return Card(
child: Container(
padding: EdgeInsets.fromLTRB(100, 0, 100, 0),
child: Center(
child: Text("Middle First"),
),
),
color: Colors.amber,
);
},
),
);
}
Widget _buildTop() {
return Card(
child: Text("Top"),
);
}
}
class BuildBottom extends StatefulWidget {
@override
State createState() => _BuildBottomState();
}
class _BuildBottomState extends State<BuildBottom> {
int _num = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
_num = timer.tick;
});
});
}
@override
Widget build(BuildContext context) {
return Flex(
direction: Axis.horizontal,
children: [
Expanded(
flex: 1,
child: Card(
color: Colors.black26,
child: Container(
padding: EdgeInsets.fromLTRB(0, 100, 0, 100),
child: Center(
child: Text("$_num"),
),
),
)),
],
);
}
}

上圖是我測試的代碼,黑框中的數(shù)據(jù)是通過定時器Timer自動更新的。
int _num = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
_num = timer.tick;
});
});
}
在initState()函數(shù)中,我們做了一件事情,就是一個初始化,并且讓黑框中數(shù)字每1s進(jìn)行一次更新。
在源碼中,這個數(shù)據(jù)更新處于兩種位置:Main頁面、組件化的BuildBottom。
- Main頁面:在這個頁面中,如果重構(gòu),就會發(fā)生我們上述所說的情況,把整個頁面全部重構(gòu)了。
-
組件化的BuildBottom:將上述的更新代碼轉(zhuǎn)移到這個組件中,那么重構(gòu)的效果就會和上述的一樣,當(dāng)然你還可以進(jìn)行細(xì)化。
性能優(yōu)化前
性能優(yōu)化后
通過Observatory的觀測,我們能夠看到兩種位置進(jìn)行了更新,他們重構(gòu)所需要進(jìn)行的步驟是完全不一樣的程度,況且該Demo的頁面邏輯還處于一個比較簡單的狀態(tài),如果是復(fù)雜頁面的話開銷則會非常龐大,對性能的影響就會比較明顯。






