Flutter性能优化:启动速度与渲染优化
1. 启动速度优化
Flutter的启动流程涉及多个关键步骤。首先,一个Native进程只包含一个Dart VM。当第一个FlutterEngine初始化时,会创建并初始化Dart VM。一个Dart VM可以承载多个FlutterEngine,每个FlutterEngine运行在独立的Isolate中,它们的内存数据不共享,需要通过Isolate事先设置的端口进行通信。
启动优化检测工具
Macrobenchmark是一种常用的启动性能检测工具,它可以帮助开发者测量和分析应用的启动时间。此外,代码类型的工具如App Startup库提供了无侵入性的初始化方案。App Startup库允许在应用启动时高效地初始化组件,通过共享单个Content Provider来减少启动时间,而不是为每个组件定义单独的Content Provider。
使用App Startup时,需要实现Initializer<T>接口。在create()方法中执行初始化操作并返回实例,在dependencies()方法中定义依赖关系以控制初始化顺序。例如,初始化Timber库的示例如下:
class TimberInitializer : Initializer<String> {
override fun create(context: Context): String {
Timber.plant(Timber.DebugTree())
return "TimberInit"
}
override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
}
}
// 调用初始化
AppInitializer.getInstance(context)
.initializeComponent(TimberInitializer::class.java)
启动优化方案
3.1 Flutter引擎预加载
在Native和Flutter混合开发场景中,通过FlutterFragment加载Flutter页面时,首次加载往往较慢。使用引擎预加载可以显著减少首次加载耗时,实现页面秒开。具体实现是在空闲时初始化FlutterEngine并缓存起来。
示例代码中,通过Looper.myQueue().addIdleHandler添加一个IdleHandler,在CPU空闲时回调queueIdle方法,在该方法中初始化FlutterEngine并缓存到集合中。预加载完成后,可以通过缓存管理器获取引擎。建议使用FlutterEngineGroup创建FlutterEngine,并在引擎初始化后立即注册Channel,以避免时序问题。
public class FlutterHelper {
public static final String FLUTTER_ENGINE = "flutter_engine";
public void preloadFlutterEngine(Context context) {
Looper.myQueue().addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
initFlutterEngine(context);
return true;
}
});
}
public synchronized FlutterEngine initFlutterEngine(Context context) {
if (!FlutterEngineCache.getInstance().contains(FLUTTER_ENGINE)) {
FlutterEngine engine = new FlutterEngine(context.getApplicationContext());
GeneratedPluginRegistrant.registerWith(engine);
FlutterBridge.init(engine); // 封装Channel服务
engine.getDartExecutor().executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault());
FlutterEngineCache.getInstance().put(FLUTTER_ENGINE, engine);
return engine;
} else {
return getFlutterEngine();
}
}
public FlutterEngine getFlutterEngine() {
if (isInitFinish()) {
return FlutterEngineCache.getInstance().get(FLUTTER_ENGINE);
} else {
throw new RuntimeException("请先初始化FlutterEngine!");
}
}
public boolean isInitFinish() {
return FlutterEngineCache.getInstance().get(FLUTTER_ENGINE) != null;
}
}
3.2 Dart VM预热
对于混合开发场景,如果不使用引擎预加载,可以通过Dart VM预热来提升Flutter引擎加载速度。这种方式对启动速度的提升不如预加载明显,但仍有价值。需要注意的是,引擎预加载和Dart VM预热都会增加内存成本,应根据应用内存状况和用户行为预测来权衡使用。
3.3 避免启动时初始化过多耗时任务
将非必要的初始化任务延迟执行,例如网络初始化可以放在闪屏页面,与用户强关联的模块或SDK可以等待用户登录后再初始化。
3.4 页面启动耗时避免在initState方法中处理
建议将耗时任务放在状态管理机制中处理,遵循“友好交互”原则,例如先展示加载动画或旧数据,待新数据处理完成后再刷新界面。
3.5 避免在build方法中执行耗时操作
build方法会被频繁调用,应避免在其中执行文件读取、数据库操作或网络请求等阻塞式操作。这些任务应通过Future转换为异步方式。对于CPU密集型操作,如图片压缩,可以使用Isolate来利用多核CPU。
2. 渲染优化
Flutter的渲染流程主要包括构建Widget、Element和RenderObject三棵树,然后遍历RenderObject树进行标脏处理,执行paint操作形成Layer Tree,最后发送给GPU线程转换为GPU指令执行。UI线程的渲染涉及build、layout和paint三个阶段。
4.1 build阶段优化
在build阶段,应保持build方法纯粹,避免副作用。同时,尽量减少Widget嵌套深度,将复杂Widget拆分为独立组件,以减少遍历节点数,提前结束树的遍历。
4.2 layout阶段优化
layout阶段计算节点大小,父节点给出宽高限制,子节点决定自身尺寸。使用Relayout boundary可以创建布局边界,防止边界外部分不必要的重新计算。在编写Widget时,尽量指定宽高以减少计算时间。例如,在使用ListView时,指定itemExtent值可以避免滑动时频繁计算高度。
class ListViewShow extends StatelessWidget {
const ListViewShow({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return ListView(
itemExtent: 30, // 指定每个子项的高度
children: <Widget>[
Container(child: Text('ListView 1')),
Container(child: Text('ListView 2')),
Container(child: Text('ListView 3')),
],
);
}
}
4.3 paint阶段优化
paint阶段对标脏的RenderObject进行重绘。Repaint boundary类似于Relayout boundary,用于隔离频繁更新的图层,防止大范围重绘。对于包含定时器等频繁更新的控件,可以在最小控件范围外包一层RepaintBoundary。
例如,一个倒计时控件可以使用RepaintBoundary来优化性能:
class HoursTitle extends StatefulWidget {
@override
HoursTitleState createState() => HoursTitleState();
}
class HoursTitleState extends State<HoursTitle> {
Timer timer;
int minutes = DateTime.now().minute;
int seconds = DateTime.now().second;
void _startTimer() {
timer?.cancel();
timer = Timer.periodic(Duration(seconds: 1), (timer) {
if (seconds == 0) {
if (minutes == 0) {
minutes = 59;
seconds = 59;
} else {
minutes--;
seconds = 59;
}
} else {
seconds--;
}
setState(() {});
});
}
@override
void initState() {
super.initState();
if (mounted) _startTimer();
}
@override
void dispose() {
timer?.cancel();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Container(
child: Row(
children: [
RepaintBoundary(
child: Container(
width: 200,
height: 30,
child: Text.rich(
TextSpan(
text: '距本小时结束',
children: [TextSpan(text: '$minutes : $seconds')],
),
),
),
),
Text('123'),
Text('465'),
],
),
);
}
}
通过以上优化措施,可以有效提升Flutter应用的启动速度和渲染性能,确保用户体验流畅。