Flutter性能优化完全指南:启动速度与渲染优化

Viewed 0

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应用的启动速度和渲染性能,确保用户体验流畅。

0 Answers