Flutter中被屏蔽的概念,Scheduler(调度器)

Android屏幕刷新机制—VSync、Choreographer-全面理解!(1) 上面讲到,Google在Android 4.1系统中对Android Display系统进行了优化:在收到VSync pulse后,将马上开始下一帧的渲染。即一旦收到VSync通知,CPU和GPU就立刻开始计算然后把数据写入buffer。本节就来讲 “drawing with VSync” 的实现——。Choreographer,意为 舞蹈编导、编舞者。在这里就是指 对CPU/GPU绘制的指导—— 收到VSync信号 才开始绘制,保证绘制拥有完整的16.6ms,避免绘制的随机性。 阅读详情

前言

一切皆Widget,良好的底层设计都会屏蔽底层的逻辑,Java如此,Flutter亦是如此,甚至还有开发者面向Getx编程,那么我们可以做如是类比,Flutter是J2EE, Getx是Spring套件,作为Java后台开发,面向Spring开发是不够的,正如,跨平台Flutter 不了解底层机制, 也无法分析和解决非逻辑问题。所以请花三分钟阅读本文,了解基础原理就好。其他相关的文章在掘金上,我就不复制了,请移步武当山王也

理解Scheduler,调度器

GUI系统,即Graphical User Interface(图形用户界面),从底层概念上,包含

  • 呈现(Canvas)
  • 交互(用户输入事件,Pointer)
  • 窗口管理

Flutter并不是一个完整的GUI系统,Flutter着重于Canvas, 交互和窗口管理都依赖运行平台的GUI。这些前置概念可以帮我们正确认识Flutter的定位,进而理解源码的边界和处理本源逻辑,不至于陷入其中,无法自拔。

Scheduler

binding.dart 粘合物
目的是连接Flutter层 SchedulerBinding 和 PlatformDispatcher
  /// Ensures callbacks for [PlatformDispatcher.onBeginFrame] and
  /// [PlatformDispatcher.onDrawFrame] are registered.
  
  void ensureFrameCallbacksRegistered() {
    // 粘合逻辑
    platformDispatcher.onBeginFrame ??= _handleBeginFrame;
    platformDispatcher.onDrawFrame ??= _handleDrawFrame;
  }

binding的主要目的有了,我们细化进入代码实现,看SchedulerBinding 主要提供了什么函数,提供什么样的服务。

1. 获取instance
  /// The current [SchedulerBinding], if one has been created.
  ///
  /// Provides access to the features exposed by this mixin. The binding must
  /// be initialized before using this getter; this is typically done by calling
  /// [runApp] or [WidgetsFlutterBinding.ensureInitialized].
  static SchedulerBinding get instance => BindingBase.checkInstance(_instance);
  static SchedulerBinding? _instance;
2. 调度任务,

这段代码并不复杂,将需要被调度的任务放入调度队列,其中Priority,按优先级由高到低分别是touch、animation、idle. 这个场景下我们暂时不考虑Flow的作用。

a. 创建任务对象,返回future,加入_taskQueue优先队列

  Future<T> scheduleTask<T>(
    TaskCallback<T> task,
    Priority priority, {
    String? debugLabel,
    Flow? flow,
  }) {
    final bool isFirstTask = _taskQueue.isEmpty;
    final _TaskEntry<T> entry = _TaskEntry<T>(
      task,
      priority.value,
      debugLabel,
      flow,
    );
    _taskQueue.add(entry);
    if (isFirstTask && !locked) {
      _ensureEventLoopCallback();
    }
    return entry.completer.future;
  }

b. 启动队列运行, 其中Timer.run()是尽可能快的执行任务。

  void _ensureEventLoopCallback() {
    assert(!locked);
    assert(_taskQueue.isNotEmpty);
    if (_hasRequestedAnEventLoopCallback) {
      return;
    }
    _hasRequestedAnEventLoopCallback = true;
    Timer.run(_runTasks);
  }

  // Scheduled by _ensureEventLoopCallback.
  void _runTasks() {
    _hasRequestedAnEventLoopCallback = false;
    if (handleEventLoopCallback()) {
      _ensureEventLoopCallback();
    } // runs next task when there's time
  }

c. 取任务队头,并执行任务, 我们可以看到队列的执行是通过_ensureEventLoopCallback和handleEventLoopCallback 形成递归取任务,终止条件是_taskQueue.isEmpty || locked。


  ('vm:notify-debugger-on-exception')
  bool handleEventLoopCallback() {
    if (_taskQueue.isEmpty || locked) {
      return false;
    }
    final _TaskEntry<dynamic> entry = _taskQueue.first;
    if (schedulingStrategy(priority: entry.priority, scheduler: this)) {
      try {
        _taskQueue.removeFirst();
        entry.run();
      } catch (exception, exceptionStack) {
        // ignored
      }
      return _taskQueue.isNotEmpty;
    }
    return false;
  }
调度frame任务

在Flutter绘制概念中,帧有两种不同的类型,分别为transient frame和persistent frame,即瞬态帧和持续帧。瞬态帧是在两个持久帧中间的帧,一般是动画,布局变化,用户输入响应, 持续帧,持续帧主要是不处于变化的帧。相对的概念即瞬态帧(也叫动画帧)。当UI不需要刷新时,持续帧会响应事件处理,布局绘制等。

  // 被调用方
  void scheduleFrame() {
    if (_hasScheduledFrame || !framesEnabled) {
      return;
    }
    ensureFrameCallbacksRegistered();
    platformDispatcher.scheduleFrame();
    _hasScheduledFrame = true;
  }
  
  void main() {
  // We use ViewRenderingFlutterBinding to attach the render tree to the window.
  ViewRenderingFlutterBinding(
    // The root of our render tree is a RenderPositionedBox, which centers its
    // child both vertically and horizontally.
    root: RenderPositionedBox(
      child: RenderParagraph(
        const TextSpan(text: 'Hello, world.'),
        textDirection: TextDirection.ltr,
      ),
    ),
  ).scheduleFrame(); // 调用方
}

上述scheduleFrame方法,为主动调用,和scheduleTask对应,通过调用平台层platformDispatcher.scheduleFrame()的方法,通知底层创建新的帧, ensureFrameCallbacksRegistered() 保证系统帧的创建可以被回调到binding.

  • scheduleForcedFrame: 忽略lifecycleState 即不关心framesEnabled
  • scheduleWarmUpFrame: 不等待系统Vsync,尽快调用。下方是一个实例。
void runApp(Widget app) {
  final WidgetsBinding binding = WidgetsFlutterBinding.ensureInitialized();
  assert(binding.debugCheckZone('runApp'));
  binding
    ..scheduleAttachRootWidget(binding.wrapWithDefaultView(app))
    ..scheduleWarmUpFrame();
}

上述主要提供给Flutter调用方,供应用使用来调度任务和帧


渲染(rendering pipeline)回调
  void addPostFrameCallback(FrameCallback callback, {String debugLabel = 'callback'}) {
    assert(() {
      if (debugTracePostFrameCallbacks) {
        final FrameCallback originalCallback = callback;
        callback = (Duration timeStamp) {
          Timeline.startSync(debugLabel);
          try {
            originalCallback(timeStamp);
          } finally {
            Timeline.finishSync();
          }
        };
      }
      return true;
    }());
    _postFrameCallbacks.add(callback);
  }

tip: 这里有两个有用的技巧,保存callback的引用,将callback指向新的对象,并放到_postFrameCallbacks, 这样可以避免UI界面被回收引发悬停指针。 第二个技巧是,可以通过asset的不同返回值,中断代码的执行。

上述代码中,Timeline.startSync() 是开启同步,涉及sky_engine, 我们暂时不做深究, 只需要明确这里的调用,具体的说addPostFrameCallback callback的调用是同步的。方法声明上解释,只会在rendering pipeline为空时,也就是最后一个持续帧调用完成后,才会回调注册的callback。我们可以验证一下。

  void handleDrawFrame() {
    assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
    _frameTimelineTask?.finish(); // end the "Animate" phase
    try {
      // PERSISTENT FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.persistentCallbacks;
      for (final FrameCallback callback in List<FrameCallback>.of(_persistentCallbacks)) {
        _invokeFrameCallback(callback, _currentFrameTimeStamp!);
      }

      // POST-FRAME CALLBACKS
      _schedulerPhase = SchedulerPhase.postFrameCallbacks;
      final List<FrameCallback> localPostFrameCallbacks =
          List<FrameCallback>.of(_postFrameCallbacks);
      _postFrameCallbacks.clear();
      Timeline.startSync('POST_FRAME');
      try {
        for (final FrameCallback callback in localPostFrameCallbacks) {
          _invokeFrameCallback(callback, _currentFrameTimeStamp!);
        }
      } finally {
        Timeline.finishSync();
      }
    } finally {
      _schedulerPhase = SchedulerPhase.idle;
      _frameTimelineTask?.finish(); // end the Frame
      assert(() {
        if (debugPrintEndFrameBanner) {
          debugPrint('▀' * _debugBanner!.length);
        }
        _debugBanner = null;
        return true;
      }());
      _currentFrameTimeStamp = null;
    }
  }

上述代码,清晰展示了handleDrawFrame的调用逻辑,在持续帧完成,并通知之后,才开始通知_postFrameCallbacks, 我们可以明确,动画的,瞬态帧并不会触发我们addPostFrameCallback添加的callback, 这也是我们可以在界面中使用他来监听界面已经渲染完成, 而不必使用延迟等不可靠的操作。

Tip: localPostFrameCallbacks的中间copy,可以保证不阻塞新事件的加入


补充:
  • 保证update是有意义的,也就是在空闲态再进行更新
  void ensureVisualUpdate() {
    switch (schedulerPhase) {
      case SchedulerPhase.idle:
      case SchedulerPhase.postFrameCallbacks:
        scheduleFrame();
        return;
      case SchedulerPhase.transientCallbacks:
      case SchedulerPhase.midFrameMicrotasks:
      case SchedulerPhase.persistentCallbacks:
        return;
    }
  }

  • 四种调度的状态
enum SchedulerPhase {
  idle,
  transientCallbacks,
  midFrameMicrotasks,
  postFrameCallbacks,
}
Flutter运行过程(一):一文搞懂Widget更新机制 Flutter运行过程(一):一文搞懂Widget更新机制 本系列将从Flutter框架runApp()运行开始,结合框架源码,分析flutter UI渲染、更新机制,布局、绘制过程,以及解析flutter主要的生命周期过程。认真读完本系列,读者一定会对Flutter运行过程了如指掌、胸有成竹。 本系列将有小量源码出没,建议读者打开编译器,配合框架源码食用,效果更佳。   开始的开始 本文主要介绍Flutter的更新机制,为了更好地理解Flutter的更新过程,首先得知道操作系统是如何协调图像的绘制和显 阅读详情

相关推荐

Flutter定位权限处理全攻略:从零到上架,搞定geolocator 10.1.0的Android/iOS配置与审核避坑

本文全面解析Flutter应用中geolocator 10.1.0的地理定位权限处理,涵盖Android/iOS双平台配置、权限申请策略及上架审核避坑指南。从依赖添加到高级性能优化,提供实战解决方案,帮助开发者高效实现精准定位功能,确保应用顺利通过各平台审核。

weixin_42519826的博客 159

Flutter 核心渲染流程分析 [完结篇]

导语 作为一个 Flutter 开发者,我们仅需组合 widget 即可实现各种不同的交互。这其中 Flutter 是如何通过 widget 完成屏幕上的呈现?Native 层起到了怎样的作用?作为 UI 核心渲染流程的完结篇,这次我们不陷于每一个细节,而是从全局梳理主要流程,把握关键节点。对于细节的知识点,我会附上一些更深入的文章。希望能对你认识 Flutter 渲染机制有所帮助。 一、缘起:setState() 无论是一个简单的列表,还是一段灵巧的动画,本质都是由一个个快速切换的画面组成,术语称其

Androidbye的博客 1019

STM32F105双路CAN通讯无法进入接收中断问题及解决方法

STM32F105RBT6双路CAN通讯无法进入接收中断问题及解决方法

m0_62383223的博客 3441

Error: Method ‘addPostFrameCallback‘ cannot be called on ‘WidgetsBinding?‘ because it is potentially

Error: Method 'addPostFrameCallback' cannot be called on 'WidgetsBinding?' because it is potentially

NewBeen1的博客 1630

有史以来最小的GIF/png/jpg/jpeg

昨天我对图像进行了 base64 编码,这样我就可以将它发送到 CouchDB 以测试我正在为客户编写的一些代码。它让我想起了我之前在远程服务器上设置 cookie 时所做的事情。基本上,一个小的 PHP 脚本被放在远程服务器上,它接受几个 GET 参数并根据它们的值设置一些 cookie。然后脚本输出一个 1x1 的透明 GIF。本地服务器上的 PHP 脚本生成了一个 IMG 标签,该标签链接到该图像并根据本地服务器上的 COOKIES 设置参数。

凌凌1301的博客 1416

面试篇 - LoRA(Low-Rank Adaptation) 原理

输出示例:trainable params: 262,144 || all params: 6,742,016,000 || trainable%: 0.0039。论文实验显示:零初始化 BB 可使初始损失与预训练模型相差不足0.1%,而非零初始化可能差50%+。若初始 ΔW≠0,模型可能立即偏离预训练学到的知识(如GPT-3突然忘记如何造句)。:如同汽车改装,先保持原厂配置(W),再逐步加装新部件(ΔW),避免直接飙车失控。:大模型的权重变化矩阵 ΔWΔW 通常是低秩的(少数主成分主导变化)。

qq_60245590的博客 1151

flutter 专题七 Flutter面试之渲染流程

Flutter面试中必问的一个面试题就是渲染相关的话题。作为Google在2018年发布的一款跨平台UI框架,使用Dart作为其开发语言,底层使用Skia图形库进行视图渲染,渲染速度和用户体验堪比原生。

m0_61164038的博客 1209

【牛股启动线】副图指标使用技术说明,图文案例分享,通达信炒股指标

如上图,在紫色线在黑色虚线上时,在出现紫色柱线时,通常会出现强势拉升的行情​。柱线加长加上紫色线上扬,进场跟随做多抓强势行情,紫色柱线和紫色线同时做多持续则可以继续持股。如上图,副图指标【牛股启动线】,指标信号包括紫色线走势信号和紫色柱线信号,以及底部的四条虚线技术信号​,还有一条绿色的参考线,一条黑色的虚线两天分档线。如上图,参考买点,紫色柱线开始出现时进场买点,在上图标记的位置对应的就是进场买点​。紫色柱线加长+紫色线上扬,多头共振持股,行情继续拉升阶段,持股待涨或顺势​进场做多。

weixin_69760830的博客 1907

Flutter 从源码扒一扒动画机制

其实还有很多源码细节,我就不去抠了,先从整体上把握整个流程我感觉就可以了。

源代码的博客 1302

Arduino、ESP32驱动BMP390L数字大气压传感器(压力传感器)

BMP390L是一款博世新研发的工业级专用数字气压传感器,具有极高的温度稳定性、低漂移、低功耗。

不脱发的程序猿 2007

基于JeecgBoot 3.9.0 开源项目的企业管理平台

基于JeecgBoot 3.9.0 开源项目的企业管理平台

宁波阿成的博客 371

Flutter 笔记 | Flutter 核心原理(四)绘制流程

Flutter中和绘制相关的对象有三个,分别是Canvas、Layer 和 Scene: Canvas:封装了 Flutter Skia 各种绘制指令,比如画线、画圆、画矩形等指令。 Layer:分为容器类和绘制类两种;暂时可以理解为是绘制产物的载体,比如调用 Canvas 的绘制 API 后,相应的绘制产物被保存在 PictureLayer.picture 对象中。 Scene:屏幕上将要要显示的元素。在上屏前,我们需要将Layer中保存的绘制产物关联到 Scene 上。 Flutter 绘制流程:

川峰的博客 1774

Python赋能,洞见先机:构建基于异步爬虫与流处理的实时舆情监控系统

我们将从核心概念入手,逐步搭建一个集。.option("es.resource", "public-opinion-%s" % time.strftime("%Y-%m-%d")) \ # 按日期分索引。| 数据采集层 |----->| 消息队列层 |----->| 流处理与分析层 |----->| 存储与可视化层 |:使用Spark Streaming的状态操作或Flink的CEP库,检测更复杂的模式,如“某关键词的负面舆情在1小时内激增500%”。

2201_76125261的博客 433

56、Flutter核心原理--Flutter启动流程和渲染管线

启动 Flutter的入口在"lib/main.dart"的main()函数中,它是Dart应用程序的起点。在Flutter应用中,main()函数最简单的实现如下: void main() => runApp(MyApp()); 可以看main()函数只调用了一个runApp()方法,我们看看runApp()方法中都做了什么: void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..attac

wywinstonwy的专栏 1万+

SAP Fiori Launchpad Tile,UI5 应用,和 PFCG Role 的对应关系

问题:已经看到了SAP Fiori Launchpad上的某个tile,如何反推后台需要配置哪个PFCG Role才能看到该tile? 这个问题是今天一个朋友在微信上问我的。因为我最近忙着学习一个新的SAP产品,所以这个问题也没有时间展开来写,本文就只做概要介绍。 下图是一个Gateway系统上打开的SAP CRM Fiori Launchpad: 如何弄清楚后台用户配置了哪个PFCG Role之后,才能看到Sales Representative(CRM)这个Group下的tile? 既然当前我使用的用

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1520

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Flutter后台定时任务(BackgroundFetch)

应用场景之一,免费电子书退出app后,后台定时从web爬取小说最新内容实现自动更新! 这里使用的是一个开源软件BackgroundFetch。BackgroundFetch的sample代码中没有太多解析,所以看起来有写费劲,这里就例子做简要说明。 BackgroundFetch其实可以分为两部分,一部分是APP退出后在后台运行的叫做HeadlessTask,一个是进入APP后再后台运行的,叫BackgroundFetch。对于sample中的代码,我们把它划分成两部分来看就简单了。 HeadlessTa

dingpwen的博客 8392

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

Flutter 的生命周期

前言 如果你是 Android、iOS、React、Vue 的开发人员,那么你一定不会对生命周期感到陌生,当你在学习 Flutter 的时候,Flutter 也有自己的生命周期。本篇文章就讲讲 Flutter 的生命周期,从而知道应该在哪里来写业务逻辑。 Flutter 的生命周期 Flutter 的生命周期分为两个部分: Widget 的生命周期 App 的生命周期 Flutter 里...

大灰狼的小绵羊哥哥的博客 3654

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

Flutter AnimationController回调原理

每个动画帧会回调一次。创建时,ticker的初始状态是禁用的。调用[start]启动。A [Ticker]可以通过将[muted]设置为true来进行沉默。在沉默状态下,时间仍然在流逝,[start]和[stop]仍然可以被调用,但是不会回调到Ticker中。按照约定,[start]和[stop]方法由[ticker]的使用者使用,[muted]属性由[TickerProvider] 控制。**总结: **每次frame绘制之前会回调到ticker,由SchedulerBinding进行驱动。

goodhighting的博客 993

在Flutter中使用后台任务调度(APP在后台保活)

在Flutter中使用后台任务调度(APP在后台保活)

weixin_51522235的博客 9101

Flutter启动流程分析

本文我们来分析下Flutter的启动流程,首先我们从main.dart文件的main函数开始: void main() => runApp(MyApp()); 复制代码 main函数则调用的是runApp函数: void runApp(Widget app) { WidgetsFlutterBinding.ensureInitialized() ..scheduleAttachRootWidget(app) ..scheduleWarmUpFrame(); } 函数中

Dream It Possible 2674

Flutter----探索setState原理

简要说明State 1. State是什么? 我们都知道,Flutter有个特性**“everything is widget”,而Widget是@immutable(不可变的),那么你就会问,我每次setState之后的Widget**树是怎么渲染的?他既然是不变的那怎么会进行重绘改变呢?那么疑问自然而然的就来了!!!! 我们知道,StatefulWidget的State会帮助我们实现Widge...

mubowen666的博客 1719

“一文读懂“系列:Android屏幕刷新机制

很多同学觉得屏幕刷新绘制知识点对他们开发不重要,没必要学习这些东西,这部分同学可能平时维护的是一些中小型项目或者应用是安装在特定设备上,只要求写写主界面,做一些简单的网络请求,业务交互相关知识,对性能这块要求不是很高,确实涉及不到太多屏幕刷新这块知识。

Eqiqi的博客 2249

如何在 Flutter 创建一个后台任务

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EHtoN4KR-1642636579852)(https://ducafecat.tech/2022/01/20/2022/flutter-run-code-in-the-background/2022-01-20-07-24-43.png)] 原文 https://www.dltlabs.com/blog/flutter-run-code-in-the-background-513940 参考 https://pub.d

会煮咖啡的猫咪 850

Flutter之SchedulerBinding简析

开始 在原生开发中(例如Android)都会强调不能阻塞主线程,但是开发中经常会遇到发送请求或者操作数据库等,这些操作都会阻塞主线程,几乎唯一办法就是用多线程处理这些工作;而在Flutter中就像跟在前端一样,Dart也是单线程IO异步,刚才所说的这些操作既不会阻塞主线程也不会打断你的代码逻辑,所以在Flutter上开发有相当高的效率。但...

weixin_34293911的博客 1600

Flutter setState流程分析

介绍 Flutter 中最常用的 Widget 是 StatelessWidget 和 StatefulWidget,分别对应于无状态的组件和有状态的组件。而 StatefulWidget 中更新状态的方法就是 setState(fn),调用该方法后,会重新调用 StatefulWidget 的 build 方法重新构建组件,达到刷新界面的效果。那么调用 setState 方法后,是通过什么的样流程走到 build 方法的呢?带着这个疑惑通过阅读源码来分析 StatefulWidget 的更新流程。 源码解

DTCloud 1813
上一篇: 一篇文章,告别Flutter状态管理争论,问题和解决
下一篇: 必看,使用Provider优雅解决Riverpod的参数依赖
PirateBrook
博客等级 码龄11年 90粉丝 40原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值