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

news/2024/4/28 6:47:55/文章来源:https://blog.csdn.net/ConcurrentHashMap/article/details/137028326

前言

一切皆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.luyixian.cn/news_show_1027246.aspx

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

相关文章

北斗短报文+4G应急广播系统:实时监控 自动预警 保护校园安全的新力量

安全无小事&#xff0c;生命重如山。学生是祖国的未来&#xff0c;校园安全是全社会安全工作的一个重要的组成部分。它直接关系到青少年学生能否安健康地成长&#xff0c;关系到千千万万个家庭的幸福安宁和社会稳定。 灾害事故和突发事件频频发生&#xff0c;给学生、教职员工…

XSS学习(cookie远程登录演示)

1.HTTP特点&#xff1a; 1.请求应答模式。 2.灵活可扩展 3.可靠传输 4.无状态。 这里给大家举一个例子&#xff1a; HTTP是无状态的&#xff0c;所按理来说我每进行一次会话&#xff0c;比如我在CSDN发一个帖子&#xff0c;好像按理来以说我都要进行一次重新登陆&#xff0…

Vue 04 Vue 中的 Ajax、slot 插槽

Vue学习 Vue 0401 Vue中的Ajax服务器准备axios使用跨域问题解决Vue-CLI 配置代理1Vue-CLI 配置代理2案例: 用户搜索vue-resource 02 slot插槽默认插槽具名插槽作用域插槽slot总结 Vue 04 B站 Vue全家桶&#xff08;BV1Zy4y1K7SH&#xff09; 学习笔记 Vue 中的 ajax 01 Vue中的…

uniApp使用XR-Frame创建3D场景(8)粒子系统

上篇文章讲述了如何将XR-Frame作为子组件集成到uniApp中使用 本片我们详细讲解一下xr-frame的粒子系统 先看源码 <xr-scene render-system"alpha:true" bind:ready"handleReady"> <xr-node visible"{{sec8}}"><xr-asset-load t…

小程序利用WebService跟asp.net交互过程发现的问题并处理

最近在研究一个项目&#xff0c;用到asp.net跟小程序交互&#xff0c;简单的说就是小程序端利用wx.request发起请求。获取asp.net 响应回来的数据。但经常会报错。点击下图的测试按钮 出现如下错误&#xff1a; 百思不得其解&#xff0c;试了若干方法&#xff0c;都不行。 因为…

京东云搭建幻兽帕鲁Palworld多人游戏联机服务器教程,1分钟开服

使用京东云服务器搭建幻兽帕鲁Palworld游戏联机服务器教程&#xff0c;非常简单&#xff0c;京东云推出幻兽帕鲁镜像系统&#xff0c;镜像直接选择幻兽帕鲁镜像即可一键自动部署&#xff0c;不需要手动操作&#xff0c;真正的新手0基础部署幻兽帕鲁&#xff0c;阿腾云atengyun.…

Machine Learning机器学习之统计分析

目录 前言 机器学习之统计分析 统计学的主要目标包括&#xff1a; 统计学核心概念&#xff1a; 统计基础&#xff1a; 训练误差&#xff1a; 常见的损失函数&#xff1a; 正则化和交叉验证 博主介绍&#xff1a;✌专注于前后端、机器学习、人工智能应用领域开发的优质创作者、秉…

网络工程师之路由交换技术篇

网络工程师之路由交换技术篇 路由交换之技术篇ARPICMPBPDUIPv6IP编址MAC其他技术点参考 以下均为个人笔记&#xff0c;摘录到csdn做备份 路由交换之技术篇 ARP Operation Code指定了ARP报文的类型&#xff0c; 包括ARP request 和ARP reply&#xff1b;取值为1或者2 &#x…

uniapp输入框事件(防抖)

一、描述 在输入框输入内容或者说输入关键词的时候&#xff0c;往往都要进行做防抖处理。如果不做防抖&#xff0c;你输入什么&#xff0c;动态绑定的数据就会保持一致。这样不好吗&#xff0c;同步获取。有个业务场景&#xff0c;如果是搜索框&#xff0c;你每次一个字符&…

Java中读取html文件转成String,展示在浏览器

这里写目录标题 第一章1.1&#xff09;pom中引入依赖和html文件示例1.2&#xff09;使用hutool工具包读取html文件转为string1.3&#xff09;页面显示 第一章 1.1&#xff09;pom中引入依赖和html文件示例 引入hutool工具包依赖 <dependency><groupId>cn.hutool&…

【Linux】 gcc(linux下的编译器)程序的编译和链接详解

目录 前言&#xff1a;快速认识gcc 1. 程序的翻译环境和执行环境 2.编译和链接 2.1翻译环境 2.2编译环境 1. 预处理 gcc -E指令 test.c&#xff08;源文件&#xff09; -o test.i&#xff08;生成在一个文件中&#xff0c;可以自己指定&#xff09; 预处理完成之后就停下来&am…

贪心算法--最大数

个人主页&#xff1a;Lei宝啊 愿所有美好如期而遇 本题链接https://leetcode.cn/problems/largest-number/description/ class Solution { public:bool static compare(int a, int b){return (to_string(a) to_string(b)) > (to_string(b) to_string(a));}bool operato…

MySQL创建表:练习题

练习题&#xff1a; 创建一个名为"students"的数据库&#xff0c;并切换到该数据库。 在"students"数据库中创建一个名为"grades"的表&#xff0c;包含以下字段&#xff1a; id: 整数类型 name: 字符串类型&#xff0c;学生姓名 subject: 字符串…

最小可行产品需要最小可行架构——可持续架构(三)

前言 最小可行产品&#xff08;MVP&#xff09;的概念可以帮助团队专注于尽快交付他们认为对客户最有价值的东西&#xff0c;以便在投入大量时间和资源之前迅速、廉价地评估产品的市场规模。MVP不仅需要考虑产品的市场可行性&#xff0c;还需要考虑其技术可行性&#xff0c;以…

计算机专业学习单片机有什么意义吗?

玩单片机跟玩计算机区别还是很大的, 单片机有众多的种类,每一种又可能有很多个系列.可以说单片机就是为了专款专用而生的.这样来达到产品成本的降低,这就是现在身边的很多的电子产品价格一降再降的原因之一.在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一…

安装paddle detection心得

一、安装PaddlePaddle conda create -n mypaddle python3.8 conda activate mypaddle python -m pip install paddlepaddle-gpu2.6.0 -i https://mirror.baidu.com/pypi/simple 请确保您的PaddlePaddle安装成功并且版本不低于需求版本。使用以下命令进行验证。 这是CUDA1…

SpringBoot项目启动成功,但是调用接口直接报NOT FOUND 404

问题描述 SpringBoot项目启动成功&#xff0c;但是调用接口直接报NOT FOUND 404 解决办法 启动类中ComponentScan(basePackages {“com.afclab”})中的扫包路径和项目路径不一样&#xff0c;导致扫不到Controller等组件&#xff0c;修改成和项目路径一样就可以解决&#xf…

8、鸿蒙学习-HAR

HAR&#xff08;Harmony Archive&#xff09;是静态共享包&#xff0c;可以包含代码、C库、资源和配置文件。通过HAR可以实现多个模块或多个工程共享ArkUI组件、资源等相关代码。HAR不同于HAP&#xff0c;不能独立安装运行在设备上。只能作为应用模块的依赖项被引用。 一、创建…

206基于matlab的无人机航迹规划(UAV track plannin)

基于matlab的无人机航迹规划(UAV track plannin&#xff09;。输入输出参数包括 横滚、俯仰、航向角&#xff08;单位&#xff1a;度&#xff09;&#xff1b;横滚速率、俯仰速率、航向角速率&#xff08;单位&#xff1a;度/秒&#xff09;&#xff1b;飞机运动速度——X右翼、…

小美的平衡矩阵(前缀和例题)

2024美团秋招&#xff0c;被这一题给难住了 美团校招笔试真题_Java工程师、C工程师_牛客网 题目&#xff1a; 解答&#xff1a; 这道题的关键点就是要计算出以某一点为矩阵右下角时&#xff0c;1的个数 我一开始是想着遍历&#xff0c;以某一点为起点&#xff08;矩阵左上角&a…