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

前言

一切皆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层 SchedulerBindingPlatformDispatcher
  /// 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,按优先级由高到低分别是touchanimationidle. 这个场景下我们暂时不考虑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. 取任务队头,并执行任务, 我们可以看到队列的执行是通过_ensureEventLoopCallbackhandleEventLoopCallback 形成递归取任务,终止条件是_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 framepersistent 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" phasetry {// 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 Frameassert(() {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,
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/770568.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#自定义控件 生成 与 加入到项目

C#自定义控件生成 在C#中&#xff0c;自定义控件通常是通过继承现有的控件类&#xff08;如UserControl、Form等&#xff09;并添加或修改其属性和方法来实现的。以下是一个简单的示例&#xff0c;演示如何创建一个自定义控件&#xff1a; 首先&#xff0c;创建一个新的Window…

荟萃分析R Meta-Analyses 1

参考&#xff1a;Harrer, M.、Cuijpers, P.、Furukawa, TA 和 Ebert, DD (2021)。 使用 R 进行荟萃分析&#xff1a;实践指南。佛罗里达州博卡拉顿和伦敦&#xff1a;Chapman & Hall/CRC Press。 ISBN 978-0-367-61007-4。 1.1什么是荟萃分析&#xff1f; 它的创始人之一 G…

电脑如何更新AMD独立显卡驱动?安装官方驱动的方法来了!

前言 有小伙伴在电脑上安装了独立显卡之后&#xff0c;总会用驱动人生或者驱动精灵等软件给独立显卡安装驱动。这种安装方法并不能说是错的&#xff0c;反正能用就行。 安装官方驱动的办法其实很简单&#xff0c;现在独立显卡一共就那么几家&#xff0c;最常见的显卡就是Nvidi…

我们是如何在 IDE 中设计 AutoDev 的 AI 编程开发智能体语言与框架?

上周微软发布了自家的 AI 编程和软件开发智能体框架&#xff1a;AutoDev&#xff0c;其与我们开发的 IDE 插件 AutoDev 有颇多的相似之处&#xff0c;特别是一些设计思路&#xff0c;以及在对于辅助软件开发任务的智能体以及一些基础设施上。 稍有不同的是&#xff1a; 交互介质…

随手笔记——禾赛Pandar64雷达旋转方向及坐标系定义

随手笔记——禾赛Pandar64雷达旋转方向及坐标系定义 手册下载

Maven学习记录

一、简介 1. Maven&#xff1a; 基于 Java 平台的项目管理和整合工具&#xff0c;将项目的开发和管理过程抽象成一个项目对象模型&#xff08;POM&#xff09;。开发人员只需要做一些简单的配置&#xff0c;Maven 就可以自动完成项目的编译、测试、打包、发布以及部署等工作。…

【NLP学习记录】Embedding和EmbeddingBag

Embedding与EmbeddingBag详解 ●&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 ●&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制 ●&#x1f680; 文章来源&#xff1a;K同学的学习圈子1、Embedding详解 Embedding是Pytorch中最基本…

备考ICA----Istio实验8---请求超时 Request Timeouts 实验

备考ICA----Istio实验8—请求超时 Request Timeouts 实验 1. 重置bookinfo环境 kubectl delete all --all --now kubectl delete dr --all kubectl delete vs --all kubectl delete gw --all kubectl apply -f istio/samples/bookinfo/platform/kube/bookinfo.yaml kubectl a…

STM32-01基于HAL库(CubeMX+MDK+Proteus)仿真开发环境搭建(LED点亮测试实例)

STM32-01基于HAL库&#xff08;CubeMXMDKProteus&#xff09;仿真开发环境搭建&#xff08;LED点亮测试实例&#xff09; 一、 开发工具版本列表二、安装过程三、实例测试&#xff08;点亮单个LED&#xff09;0、功能需求分析1、Proteus绘制电路原理图2、STMCubeMX 配置引脚及模…

微服务day07 -- 搜索引擎 ( 数据聚合 + 自动补全 + 数据同步 + ES集群 )

1.数据聚合 聚合&#xff08;aggregations&#xff09;可以让我们极其方便的实现对数据的统计、分析、运算。例如&#xff1a; 什么品牌的手机最受欢迎&#xff1f; 这些手机的平均价格、最高价格、最低价格&#xff1f; 这些手机每月的销售情况如何&#xff1f; 实现这些…

【C语言基础】:内存操作函数

文章目录 一、memcpy函数的使用和模拟实现1.1 memcpy函数的使用1.2 memcpy函数的模拟实现 二、memmove函数的使用和模拟实现2.1 memmove函数的使用2.2 memmove函数的模拟实现 三、memset函数的使用3.1 menset函数的使用 四、memcmp函数的使用4.1 memcmp函数的使用 学海无涯苦作…

机器学习的核心算法 - CNN的原理探讨

很悲哀&#xff0c;类似这样的技术性问题讨论&#xff0c;和其他很多我感兴趣的问题&#xff0c;我现在很多时候只能采用异步模式&#xff0c;比如发帖来解决&#xff0c;然后实时的交互&#xff0c;只能跟GPT聊。我找不到合适的朋友&#xff0c;对相关话题感兴趣&#xff0c;并…

Prompt提示工程上手指南:基础原理及实践(四)-检索增强生成(RAG)策略下的Prompt

前言 此篇文章已经是本系列的第四篇文章&#xff0c;意味着我们已经进入了Prompt工程的深水区&#xff0c;掌握的知识和技术都在不断提高&#xff0c;对于Prompt的技巧策略也不能只局限于局部运用而要适应LLM大模型的整体框架去进行改进休整。较为主流的LLM模型框架设计可以基…

图解Kafka架构学习笔记(二)

kafka的存储机制 https://segmentfault.com/a/1190000021824942 https://www.lin2j.tech/md/middleware/kafka/Kafka%E7%B3%BB%E5%88%97%E4%B8%83%E5%AD%98%E5%82%A8%E6%9C%BA%E5%88%B6.html https://tech.meituan.com/2015/01/13/kafka-fs-design-theory.html https://feiz…

实现使用C#代码完成wifi的切换和连接功能

实现使用C#代码完成wifi的切换和连接功能 代码如下&#xff1a; namespace Wifi连接器 {public partial class Form1 : Form{private List<Wlan.WlanAvailableNetwork> NetWorkList new List<Wlan.WlanAvailableNetwork>();private WlanClient.WlanInterface Wla…

web前端之罗盘时钟、不一样的补零方式、LED字体、padStart

MENU 效果图htmlJavaScriptstyle 效果图 html <div class"clock"><div class"second-box"></div><div class"minute-box"></div><div class"hour-box"></div><div class"day-box&…

【P1012 [NOIP1998 提高组] 拼数】

题目描述 设有 n n n 个正整数 a 1 … a n a_1 \dots a_n a1​…an​&#xff0c;将它们联接成一排&#xff0c;相邻数字首尾相接&#xff0c;组成一个最大的整数。 输入格式 第一行有一个整数&#xff0c;表示数字个数 n n n。 第二行有 n n n 个整数&#xff0c;表示…

华为数通方向HCIP-DataCom H12-821题库(多选题:181-200)

第181题 在BGP中Community属性为可选过渡属性,是一种路由标记,用于简化路由策略的执行。它分为自定义团体属性和公共团体属性,那么以下属于公共团体属性的是哪些项? A、No_Export_Subconfed B、No_Advertise C、No_Export D、Internet 【正确答案】ABCD 【答案解析】 第18…

WPF 界面变量绑定(通知界面变化)

1、继承属性变化接口 public partial class MainWindow : Window, INotifyPropertyChanged {// 通知界面属性发生变化public event PropertyChangedEventHandler PropertyChanged;private void RaisePropertyChanged(string propertyName){PropertyChangedEventHandler handle…

Ftrans安全数据摆渡系统 构建便捷的内外网数据交换通道

安全数据摆渡系统是一种设计用于解决内外网环境下&#xff0c;数据传输、管理、共享问题的安全系统&#xff0c;通过加密、访问控制等策略&#xff0c;提供安全可靠的数据传输和共享服务&#xff0c;尤其适用于对网络安全建设要求高的行业&#xff0c;比如研发型企业、党政机构…