Flutter : Flutter 架构概览(节选)

写在前面

本篇主要介绍 Flutter 整体框架的一些概念部分。

内容

架构层

Flutter 是一个可拓展的,分层的系统。它有一系列独立的库,并且这些库依赖于下层。没有一个层有权限访问其下层(即只有下层提供数据给上层,让上层去使用,比方说 Ticker 的变化是由下层 VSync 引起的)。Framework 层里的每一部分都被设计成可选并且是可替换的。

这是官方的框架分层图:
在这里插入图片描述

Embedder

Embedder 层是根据平台采用特定语言来编写,它提供了一个入口:协调底层系统用于访问相关服务,例如布局渲染,输入,消息事件循环等。目前 Android 使用 Java 和 C++,iOS 和 Mac OS 使用 Objective-C/Objective-C++,Linux 和 Windows 使用 C++。通过 Embedder,Flutter 代码就可以以 module 的形式集成到一个已存在的应用(混合开发),或是整个应用的代码都是 Flutter 代码(纯Flutter应用)。
这也是Flutter可以在各个平台运行的原因,因为有一个相应平台的 Embedder 来让 Flutter 跟平台进行交互。

Engine

Flutter 的核心是 Flutter Engine,大部分是用 C++ 写的。当一个新的帧需要被绘制的时候,引擎就会负责栅格化合成场景。它针对 Flutter 的核心 API 提供了低级实现,包括图像(通过 Skia),文字布局,文件和网络 I/O,辅助功能支持,插件架构,还有 Dart 运行时和编译工具链。

Engine 通过 dart:ui暴露给 Flutter Framework 层, dart:ui将底层 C++ 包裹进 Dart 类里,它暴露了低级的原始实现,例如用于驱动输入、图形和文字渲染子系统的类。

可以预览下dart:ui里有什么东西:

library dart.ui;

import 'dart:async';
import 'dart:collection' as collection;
import 'dart:convert';
import 'dart:developer' as developer;
import 'dart:io'; // ignore: unused_import
import 'dart:isolate' show SendPort;
import 'dart:math' as math;
import 'dart:nativewrappers';
import 'dart:typed_data';

part 'annotations.dart';
part 'channel_buffers.dart';
part 'compositing.dart';
part 'geometry.dart';
part 'hash_codes.dart';
part 'hooks.dart';
part 'isolate_name_server.dart';
part 'key.dart';
part 'lerp.dart';
part 'natives.dart';
part 'painting.dart';
part 'platform_dispatcher.dart';
part 'plugins.dart';
part 'pointer.dart';
part 'semantics.dart';
part 'text.dart';
part 'window.dart';

Framework

一般开发者都是通过 Flutter Framework 层跟 Flutter 打交道,在 Framework 层里从下到上,我们有:

  • 基础类(Foundation),构建块服务例如 animation(使用package:flutter/animation.dart), painting(使用package:flutter/painting.dart), 和 gestures(使用 package:flutter/gestures.dart),主要是提供了对底层基础的抽象使用封装。
  • rendering layer(使用 package:flutter/rendering.dart)提供了对布局的抽象处理方式。也就是我们可以操作渲染对象。
  • widgets layer(使用package:flutter/widgets.dart),rendering layer 里的每一个渲染对象在 widgets layer 里都有一个相应的对象。widget layer 允许你定义可以复用的组合类
  • MaterialCupertino 库实现了 Material 和 iOS 设计语言的一系列的 widget。

渲染和布局

从用户输入到 GPU

Flutter 在渲染管道的首要原则就是简单即是快。Flutter 拥有一条如何让数据流动到系统的直接管道,如下图:

Build:从 Widget 到 Element

Container(
  color: Colors.blue,
  child: Row(
    children: [
      Image.network('https://www.example.com/1.png'),
      const Text('A'),
    ],
  ),
);

Conatiner 在这里设置了 color 属性,从其源码可以看到:

if (color != null)
  current = ColoredBox(color: color!, child: current);

它是插入了一个 ColoredBox 来表示颜色。

因此,Image 和 Text 也类似,插入的是 RawImage 和 RichText。于是实际上应该是这样:
在这里插入图片描述

这也是为什么我们用调试工具的时候,看到的结构比我们实际的代码还要更深。

在 build 阶段,Flutter 把 Widget 从代码转成相应的 element tree。每一个 element 代表一个在给定位置的特定的 widget。这里有两种基本类型的 element:

  • ComponentElement,作为装有其它 element 的宿主
  • RenderObjectElement,参与布局或绘制阶段的 element

没有一个应用从头到尾只用一个 widget 来绘制。因此任何一个 UI 框架最重要的一个部分就是有效地布局 widget 的层次结构,在它们被渲染到屏幕之前,确定每个元素的大小和位置。

在渲染树里每个节点的基本类是 RenderObject,它定义了一个用于布局和绘制的抽象模型。它通用到不适用固定数量的像素甚至是笛卡尔坐标系(它的一个继承者 RenderBox 就实现了笛卡尔坐标系)。每一个 RenderObject 知道它的 parent,但对于它的 children,除了知道如何访问它们和它们的约束,其它的一概不知。这让 RenderObject 可以足够地抽象来处理各种用例。

在 build 阶段,Flutter 为 element tree 里的每个 RenderObjectElement 创建或更新一个继承自 RenderObject 的对象。 RenderParagraph 用于渲染文字,RenderImage 用于渲染图像,还有RenderTransform 在绘制它的 child 之前用于变换。

布局和渲染

大多数 Flutter widget 是通过继承自 RenderBox 的对象来渲染,RenderBox 是一个实现了 2D 笛卡尔坐标系的 RenderObject。它提供了一个盒子约束模型,为每个被渲染的 widget 建立了最小和最大的宽高限制。

在布局的时候,Flutter 在渲染树里以深度优先将尺寸约束从父传递到子。在确定其大小的时候,子必须遵守从父那里拿到的约束。然后子在从父那里拿到的约束的前提下,将自己的尺寸传递给父(即向下传递约束,向上传递尺寸)。

在这样一轮走过后,每一个对象都在其父的约束下确定了尺寸,然后准备通过调用 paint() 方法进行绘制。

盒子约束模型是一种将对象在 O(n) 时间复杂度里进行布局的强有力的方式:

  • 父可以通过将最大和最小约束设置为相同的值来指定子的大小
  • 父可以指定指定子的宽度,但让它有灵活的高度(反之一样)

如果子想要知道自己的约束,可以使用 LayoutBuilder 来查看:

Widget build(BuildContext context) {
  return LayoutBuilder(
    builder: (context, constraints) {
      if (constraints.maxWidth < 600) {
        return const OneColumnLayout();
      } else {
        return const TwoColumnLayout();
      }
    },
  );
}

所有 RenderObject 的根是 RenderView,它表示渲染树的所有输出。当平台需要渲染一个新的帧时(例如一个垂直信号),RenderView 的 compositeFrame()方法就会被调用。它创建了一个 SceneBuilder 去更新场景。当场景完成的时候,RenderView 对象会把合成好的场景传递给 dart:ui里的 Window.render()方法,让 GPU 来渲染它。

class RenderView extends RenderObject with RenderObjectWithChildMixin<RenderBox> {
...
  void compositeFrame() {
    Timeline.startSync('Compositing', arguments: timelineArgumentsIndicatingLandmarkEvent);
    try {
      final ui.SceneBuilder builder = ui.SceneBuilder();
      final ui.Scene scene = layer!.buildScene(builder);
      if (automaticSystemUiAdjustment)
        _updateSystemChrome();
      _window.render(scene);
      scene.dispose();
      assert(() {
        if (debugRepaintRainbowEnabled || debugRepaintTextRainbowEnabled)
          debugCurrentRepaintColor = debugCurrentRepaintColor.withHue((debugCurrentRepaintColor.hue + 2.0) % 360.0);
        return true;
      }());
    } finally {
      Timeline.finishSync();
    }
  }
...
}

参考

Flutter architectural overview

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值