Flutter runApp到渲染上屏分析详解

目录
  • 起源
  • 分析准备
    • ensureInitialized
    • scheduleAttachRootWidget
    • scheduleWarmUpFrame
  • 总结

起源

flutter作为一个跨平台的框架,在绘制上体现出了它跨平台的良好性能.那么,它是如何从runApp()后 绘制上屏的呢?本文将与你一起去探索这一过程.

ps: 为了思维不中断, 本文仅对整体流程作分析,不会深入分析具体实现

我们运行一个flutter app ,入口一定是从runApp() 中进行的. 那么flutter 在runApp() 中做了哪些处理呢? 首先,我们从runApp() 这个函数聊起.它是一个需要传入Widget 的函数.而传入的Widget ,即首屏渲染所需的Widget.

在此我们应该知道这个概念, 即widget 是flutter 中用来描述ui如何绘制的配置文件,去形容一个组件在整体中的位置、大小.

那么不难推断出.在runApp() 的过程中,如果Widget是绘制的配置文件. 那么手势注册、桢调度等都应该是在此时注册的. 带着这样的推断我们去源码中找答案.

分析准备

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
  // 这里解释下:
  // ..是flutter中的级联运算符
  // 可以同一个对象上连续调用多个对象的变量或方法
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

在runApp() 中可以看到, 这里实际上也就是调用了三个方法,以下我们对每个方法进行刨析.

ensureInitialized

从字面意思看,这是为了确保已经初始化而调用的方法.它的作用是为了返回WidgetsBinding的对象.如果了解单列模式的话,会发现这么写实际上就是一个单列模式.

  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding._instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  }

这里我们去挖掘一下WidgetsFlutterBinding内部的构造函数在初始化时做了什么? 它是继承BingingBase的, 我们进入BingingBase中浅看一下大概的实现, Timeline 和assert这部分代码我们可以忽略.

ps: assert 在release代码中不会执行

也就是实际上的结构是这样的

 BindingBase() {
    initInstances();
    initServiceExtensions();
  }
  • initInstances() 方法是为了绑定初始化实例和其他的一些状态.
  • initServiceExtensions() 方法是为了绑定初始化服务

这里我们回过头来看WidgetsFlutterBinding它的一些实现接口, 顺序依次是:

接口 解释
GestureBinding 实现点击命中测试
SchedulerBinding 引入了帧的概念
ServicesBinding 提供对插件的访问
PaintingBinding 解码图像
SemanticsBinding 语义树
RendererBinding 处理render tree
WidgetsBinding 处理widget tree

内部的具体实现这里不再赘述,后续会逐章对这些进行分解、解释.这里只是去分析整体的流程. 也就是说, 在这里我们完成了对app系统的初始化、推动界面的绘制,获取手势等等.

scheduleAttachRootWidget

在一系列服务注册完之后,我们需要把当前的root widget 挂载到树上,

  void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }

在attachRootWidget 的方法中, 我们构建了RenderObjectToWidgetAdapter 的对象. 通过RenderObjectToWidgetAdapter 当作Element 和RenderObject 之间的桥梁,

{
    final bool isBootstrapFrame = renderViewElement == null;
    _readyToProduceFrames = true;
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
    ).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
    if (isBootstrapFrame) {
      SchedulerBinding.instance.ensureVisualUpdate();
    }
}

同时,这里 根据renderViewElement 有没有赋值来判断是否是第一次加载.如果是第一次加载页面,会通知界面去刷新ui

scheduleWarmUpFrame

这个方法从字面意思来看 应该是在界面启动时去执行的一些方法. 首先,我们看一下它的一些引用路径.发现一共有三个地方的代码都引用了这个方法

可以看到调用的三个地方分别是:

  • allowFirstFrame()
  • performReassemble()
  • runApp()

好家伙,这不都是类似于绘制的入口函数? 因此,我们可以推断这里就是一些绘制时初始化时候必须执行的一些代码.

{
    // 这里通过伪代码简要了解一下大致实现
    //
    if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) return;
    _warmUpFrame = true;
    // 这里把源代码提前了,由于handle*()的代码都是通过Timer.run执行的,实际上是
    // 一种异步执行,会在下一帧去调用
    lockEvents(() async {
      await endOfFrame;
      timelineTask.finish();
    });
    // 开始帧回调
    handleBeginFrame(null);
    // 新帧回调处理
    handleDrawFrame();
    // 这里和热重载相关
    resetEpoch();
    _warmUpFrame = false;
    //
    if (hadScheduledFrame) scheduleFrame();
}

总结

总结一下, runApp() 通过

  • 注册各种服务
  • 注册ui
  • 绘制上屏

最终在屏幕上呈现出ui,其中还有如PipelineOwner、BuildOwner等等非常重要的api,这里暂且不表. 后续我们单章详细介绍,希望这一次与你一起阅读的思路可以帮你一起思考启动的流程.

以上就是Flutter runApp到渲染上屏分析详解的详细内容,更多关于Flutter runApp渲染上屏的资料请关注我们其它相关文章!

(0)

相关推荐

  • Flutter在项目中使用动画不使用包实现详解

    目录 前言 正文 1 按下按钮柔软的感觉 2 想要一个像 Instagram 一样的喜欢按钮吗? 3 动画页面过渡 4 动画文字 5 更改/闪动文本样式 前言 动画对于 web 和移动应用程序都非常重要.但是在移动应用程序中不应该使用夸张的动画.简单但是很多动画使你的应用程序更好用.以至于当你点击一个按钮时,一种平滑的感觉或者页面过渡都会影响到你. 正文 1 按下按钮柔软的感觉 class _CustomButtonState extends State<CustomButton> with

  • flutter Bloc add两次只响应一次问题解析

    目录 问题描述 原因分析 处理方式 问题描述 连续调用两次addEvent,结果最终只能响应一次,第二次事件无法响应. @override Stream<SomeState> mapEventToState(SomeEvent event) async*{ if(event is InCreaseEvent){ state.num ++; yield state; } } someBloc.add(InCreaseEvent()); someBloc.add(InCreaseEvent());

  • Flutter 移动程序安全性提高的八个建议

    目录 正文 1. Obfuscate code 混淆代码 2. background snapshots 后台快照 3. Stay up-to-date 更新程序 4. Flushing in-memory cache 刷新内存缓存 5. local authentication 本地认证 6. Secure Storage 安全储存 7. Restrict network traffic 限制网络流量 8. jail-breaking protection 越狱保护 正文 这里有一些步骤,我们

  • flutter Bloc 实现原理示例解析

    目录 序言 1. 事件流 > 状态流 (中转) 2. 使用 BlocBuilder 实时监听状态变更, 如何实现的呢? 总结 扩展 序言 在flutter开发中,我们使用 bloc 框架,基于状态变更进行响应式开发.本篇文章,小轰将 bloc 核心业务块进行拆解简化,聊一聊它的实现思想,bloc 核心能力分为如下两点: 添加事件 event,将 '事件流' 转换为 '状态流' state 监听 bloc 流,每次 state 状态变更,通知 widget 更新 下面,用自定义Bloc的方式,来给

  • 封装flutter状态管理工具示例详解

    目录 引言 RxBinder 代码实现 Demo 完美运行 引言 关于 Flutter 状态管理,公司项目使用的是Bloc方案.Bloc 其实本质上是 provider 的封装扩展库,整体通过 InheritedWidget .Notifier 外加 Stream中转实现状态变更通知. 关于 Bloc 实现原理,有兴趣的同学可以观看这篇文章 Bloc原理解析 RxBinder 撇开Bloc内部实现策略,小轰尝试基于数据驱动模型,自定义一套状态管理工具.构思如下: 主要成员如下: RxBinder

  • Flutter 阻止系统键盘弹出的优雅方式

    目录 前言 系统键盘弹出的原因 拦截系统键盘弹出信息 画自己的键盘 结语 前言 开篇先吐槽一下,输入框和文本,一直都是官方每个版本改动的重点,先不说功能上全不全的问题,每次版本升级,必有 breaking change .对于 extended_text_field | Flutter Package (flutter-io.cn) 和 extended_text | Flutter Package (flutter-io.cn) 来说,新功能都是基于官方的代码,每次版本升级,merge 代码就

  • Flutter runApp到渲染上屏分析详解

    目录 起源 分析准备 ensureInitialized scheduleAttachRootWidget scheduleWarmUpFrame 总结 起源 flutter作为一个跨平台的框架,在绘制上体现出了它跨平台的良好性能.那么,它是如何从runApp()后 绘制上屏的呢?本文将与你一起去探索这一过程. ps: 为了思维不中断, 本文仅对整体流程作分析,不会深入分析具体实现 我们运行一个flutter app ,入口一定是从runApp() 中进行的. 那么flutter 在runApp

  • Android Flutter实现搜索的三种方式详解

    目录 示例 1 :使用搜索表单创建全屏模式 编码 示例 2:AppBar 内的搜索字段(最常见于娱乐应用程序) 编码 示例 3:搜索字段和 SliverAppBar 编码 结论 示例 1 :使用搜索表单创建全屏模式 我们要构建的小应用程序有一个应用程序栏,右侧有一个搜索按钮.按下此按钮时,将出现一个全屏模式对话框.它不会突然跳出来,而是带有淡入淡出动画和幻灯片动画(从上到下).在圆形搜索字段旁边,有一个取消按钮,可用于关闭模式.在搜索字段下方,我们会显示一些搜索历史记录(您可以添加其他内容,如建

  • Flutter应用框架搭建之屏幕适配详解

    目录 原理 flutter_screenutil 添加依赖 初始化 使用 效果 其他 Api 因移动设备的多样性,特别是 Android 的碎片化严重,存在各种各样的分辨率,而 Flutter 跨平台开发又需同时支持 Android 和 iOS ,为尽可能的还原设计图效果提升用户体验,屏幕适配就势在必行了. Flutter 暂时没有官方的屏幕适配方案,在 Flutter 项目开发中目前大部分的适配方案都是通过比例来进行适配,是一个通用的适配方法,该适配方法也在前端.Android.iOS.小程序

  • Flutter利用Canvas绘制精美表盘效果详解

    目录 前言 初始化 面板 刻度 刻度线 刻度值 指针 时针 分针 秒针 动起来 前言 趁着周末空闲时间使用 Flutter 的 Canvas制作了一个精美表盘. 最终实现的效果还不错,如下: 前面说到使用 Canvas 实现该表盘效果,而在 Flutter 中使用 Canvas 更多的则是继承 CustomPainter 类实现 paint 方法,然后在 CustomPaint 中使用自定义实现的 CustomPainter. 比如这里创建的 DialPainter 使用如下: @overrid

  • Flutter Module添加到iOS项目示例详解

    目录 1. 创建flutter module 2. flutter 模块嵌入原生应用 3. flutter模块和原生通信 小结 1. 创建flutter module 摘要:我们实际开发开始作为混合开发,可能会把一个模块使用flutter开发,之后嵌入到iOS项目中.比如说我们的商城模块使用flutter开发,这样android和iOS都可以使用. 我们通常会把iOS项目和 flutter module在同一目录,我们创建一个商城的module flutter create --template

  • React commit源码分析详解

    目录 总览 commitBeforeMutationEffects commitMutationEffects 插入 dom 节点 获取父节点及插入位置 判断当前节点是否为单节点 在对应位置插入节点 更新 dom 节点 更新 HostComponent 更新 HostText 删除 dom 节点 unmountHostComponents commitNestedUnmounts commitUnmount commitLayoutEffects 执行生命周期 处理回调 总结 总览 commit

  • Flutter最小刷新范围探索ValueListenableBuilder使用详解

    目录 引子 ValueListenableBuilder 如何用 怎么做 不足点 引子 管理对象太多? 刷新管理太麻烦 ? Flutter刷新范围控制不好 ? 不妨看看本文 , 希望提供给你一些思路吧 ! 说起 Flutter 刷新, 你的第一印象是什么 ? setState ? 是的, 只要使用过 Flutter 的人, 第一印象都必然是 setState , 但是由于 setState 滥用的问题, 性能问题就脱颖而出. 因此产出了诸如 Fish_redux 之流, 这些框架尽可能的使用 S

  • Flutter快速制作一个水印组件实例详解

    目录 正文 通过child属性将水印叠加给子组件 createWatermark方法 Watermark组件的完整代码 正文 项目开发的过程中,经常会遇到添加水印的需求,其作用无非就是防止重要信息通过截图外传.(虽然我觉得并没有什么卵用,但领导的需求是不容质疑的) 那么,作为一线码农的我,也只能屁颠屁颠的开搞. 通过child属性将水印叠加给子组件 水印组件,既然是组件,就是需要发扬Flutter套娃的精神,通过child属性将水印叠加给子组件. class Watermark extends

  • Android 欢迎全屏图片详解及实例代码

    Android 欢迎全屏图片详解 其实欢迎界面就是在主Activity之前再添加一个欢迎的Activity.在这个Activity中实现欢迎界面,和其他的Activity用法 是基本一样,只有细微的差别.     1.在Activity的onCreate方法中实现: @Override ic void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /**全屏设置,隐藏窗口所有装饰**/ getW

  • Java逃逸分析详解及代码示例

    概念引入 我们都知道,Java 创建的对象都是被分配到堆内存上,但是事实并不是这么绝对,通过对Java对象分配的过程分析,可以知道有两个地方会导致Java中创建出来的对象并一定分别在所认为的堆上.这两个点分别是Java中的逃逸分析和TLAB(Thread Local Allocation Buffer)线程私有的缓存区. 基本概念介绍 逃逸分析,是一种可以有效减少Java程序中同步负载和内存堆分配压力的跨函数全局数据流分析算法.通过逃逸分析,Java Hotspot编译器能够分析出一个新的对象的

随机推荐