StatelessWidget
abstract class StatelessWidget extends Widget {
/// Initializes [key] for subclasses.
const StatelessWidget({ Key key }) : super(key: key);
…
@override
StatelessElement createElement() => StatelessElement(this);
…
}
StatefulWidget
abstract class StatefulWidget extends Widget {
/// Initializes [key] for subclasses.
const StatefulWidget({ Key key }) : super(key: key);
…
@override
StatefulElement createElement() => StatefulElement(this);
…
}
确实可以看出,都会创建Element
,只是StatelessWidget
和StatefulWidget
所创建的Element
类型不一样,这里就先不深入了。上面可以知道widget
和element
存在对应的关系,那下面看看element
。
2.Element
看看官方开发者文档中开篇看到:
An instantiation of a Widget at a particular location in the tree.
意思是:element
是树中特定位置的widget
实例。这里描述的很明显,也就是Widget
是总监,部署技术规划,而element
就是员工,真正干活。
widget
描述如何配置子树,由于widgets
是不可变的,所以可以用相同的widget
来同时配置多个子树,Element
表示widget
配置树中的特定位置的实例,随着时间的推移,和给定的Element
关联的Widget
可能会随时变化,例如,如果父widget
重建并为此位置创建新的widget
。
Elements
构成一棵树,大多数elements
都会有唯一的孩子,但是一些widgets
(如RenderObjectElement)可以有多个孩子。
** Element具有以下生命周期:**
- framework通过调用即将用来作
element
的初始化配置信息的Widget
的Widget.createElement方法来创建一个element
。 - framework通过调用mount方法以将新创建的Element添加到给定父级中给定槽点的树上。 mount方法负责将任何子
Widget
扩充到Widget
并根据需要调用attachRenderObject
,以将任何关联的渲染对象附加到渲染树上。 - 此时,
element
被视为激活
,可能出现在屏幕上。 - 在某些情况下,父可能会更改用于配置此
Element
的Widget,例如因为父重新创建了新状态。发生这种情况时,framework将调用新的Widget
的update方法。新Widget
将始终具有与旧Widget
相同的runtimeType和key属性。如果父希望在树中的此位置更改Widget
的runtimeType或key,可以通过unmounting(卸载)此Element并在此位置扩充新Widget
来实现。 - 在某些时候,祖先(Element)可能会决定从树中移除该
element
(或中间祖先),祖先自己通过调用deactivateChild来完成该操作。停用中间祖先将从渲染树中移除该element
的渲染对象,并将此element
添加到所有者属性中的非活动元素列表中,从而framework调用deactivate方法作用在此element
上。 - 此时,该
element
被视为“无效”,不会出现在屏幕上。一个element
直到动画帧结束前都可以保存“非活动”状态。动画帧结束时,将卸载仍处于非活动状态的所有element
。 - 如果
element
被重写组合到树中(例如,因为它或其祖先之一有一个全局建(global key)被重用),framework将从所有者的非活动elements
列表中移除该element
,并调用该element
的activate方法,并重新附加到element
的渲染对象到渲染树上。(此时,该元素再次被视为“活动”并可能出现在屏幕上) - 如果
element
在当前动画帧的末尾(最后一帧)没有被重新组合到树中,那么framework将会调用该元素的unmount方法。
这里可以知道element
的生命周期。并且平时开发没有接触到Element
,都是直接操控widget
,也就是说Flutter已经帮我们对widget
的操作映射到element
上,我这里想象到的有点事降低开发复杂。下面结合一个例子(绘制Text),看看element
是不是最后渲染出来的view:
new Text(“hello flutter”);
下面看下new Text
的源码:
…
@override
Widget build(BuildContext context) {
final DefaultTextStyle defaultTextStyle = DefaultTextStyle.of(context);
TextStyle effectiveTextStyle = style;
if (style == null || style.inherit)
effectiveTextStyle = defaultTextStyle.style.merge(style);
if (MediaQuery.boldTextOverride(context))
effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold));
Widget result = RichText( ---->Text原来是通过RichText这个widget来构建树
textAlign: textAlign ?? defaultTextStyle.textAlign ?? TextAlign.start,
textDirection: textDirection, // RichText uses Directionality.of to obtain a default if this is null.
locale: locale, // RichText uses Localizations.localeOf to obtain a default if this is null
softWrap: softWrap ?? defaultTextStyle.softWrap,
overflow: overflow ?? defaultTextStyle.overflow,
textScaleFactor: textScaleFactor ?? MediaQuery.textScaleFactorOf(context),
maxLines: maxLines ?? defaultTextStyle.maxLines,
strutStyle: strutStyle,
text: TextSpan(
style: effectiveTextStyle,
text: data,
children: textSpan != null ? [textSpan] : null,
),
);
…
继续往RichText
里面看:
…
@override
RenderParagraph createRenderObject(BuildContext context) {
assert(textDirection != null || debugCheckHasDirectionality(context));
//返回RenderParagraph widget
return RenderParagraph(text,
textAlign: textAlign,
textDirection: textDirection ?? Directionality.of(context),
softWrap: softWrap,
overflow: overflow,
textScaleFactor: textScaleFactor,
maxLines: maxLines,
strutStyle: strutStyle,
locale: locale ?? Localizations.localeOf(context, nullOk: true),
);
}
…
发现最终它会返回RenderParagraph
widget,继续往里看:
class RichText extends LeafRenderObjectWidget {
}
可以发现RichText
继承LeafRenderObjectWidget
,继续往下看:
//用于配置RenderObject子类的RenderObjectWidgets的超类,没有孩子,也就是没有字节点(child)
abstract class LeafRenderObjectWidget extends RenderObjectWidget {
const LeafRenderObjectWidget({ Key key }) : super(key: key);
//构建出类型是LeafRenderObjectElement
@override
LeafRenderObjectElement createElement() => LeafRenderObjectElement(this);
}
可以看到LeafRenderObjectWidget
是抽象类,并且继承了RenderObjectWidget
,继续往下看:
/// RenderObjectWidgets provide the configuration for [RenderObjectElement]s,
/// which wrap [RenderObject]s, which provide the actual rendering of the
/// application.
//上面注释是:RenderObjectWidgets向[RenderObjectElement]提供了配置信息,包装了[RenderObject],在应用程序了提供了实际的渲染
abstract class RenderObjectWidget extends Widget {
const RenderObjectWidget({ Key key }) : super(key: key);
//创建element
@override
RenderObjectElement createElement();
//创建RenderObject
@protected
RenderObject createRenderObject(BuildContext context);
//更新RenderObject
@protected
void updateRenderObject(BuildContext context, covariant RenderObject renderObject) { }
//卸载RenderObject
@protected
void didUnmountRenderObject(covariant RenderObject renderObject) { }
}
由注释可以知道RenderObject
才是绘制UI背后的真正对象,那下面继续简单跟踪:
3.RenderObjectWidget
先看看RenderObjectWidget
继承关系:
看看文档的一一介绍:
3.1.SingleChildRenderObjectWidget
官方文档写的很清楚: 是RenderObjectWidgets
的超类,用于配置有单个孩子的RenderObject
子类(为子类提供存储,实际不提供更新逻辑),并列了具体的实际widget
,如常用的Align、ClipRect、DecoratedBox都是属于这类。
3.2.MultiChildRenderObjectWidget
同样也是RenderObjectWidgets
的超类,用于配置有单个children(也就是多个child)的RenderObject
子类,如Flex、Flow、Stack都属于这类。
3.3.LeafRenderObjectWidget
同样也是RenderObjectWidgets
的超类,用于配置没有孩子的RenderObject
子类,如:RichText(平时的Text)、RawImage、Texture等。 这里总结一下:
SingleChildRenderObjectWidget
用作只有一个child的widget
。MultiChildRenderObjectWidget
用作有children(多个孩子)的widget
。LeafRenderObjectWidget
用作没有child的widget
。RenderObjectWidget
定义了创建,更新,删除RenderObject的方法,子类必须实现,RenderObject
是最终布局、UI渲染的实际对象。
那么假如我现在界面上,假如布局如下:
那么渲染流程如下:
这时候会想:为什么要加中间层Element
呢,不直接由Widget
直接转换成RendObject
不是直接更好么?其实并不是这样的。首先知道Flutter是响应式框架,在某一个时刻,可能会受到不同的输入流影响,中间层Element
对这一时刻的事件做了汇总,最后将需要修改的部分同步到RendObject
tree上,也就是:
- 尽可能的降低
RenderObject
tree的更改,提高页面渲染效率。 - 没有直接操作UI,通过数据驱动视图,代码更容易理解和简洁。
上面得出UI树是由一个个element
节点组成,但是最终的渲染是通过RenderObject
来完成,一个widget
从创建到显示到界面的流程是:widget生成element,然后通过createRenderObject方法创建对应的RenderObject
关联到Element.renderObject
上,最后通过RenderObject
来完成绘制。
三、启动到显示
上面知道,widget
并不是真正显示的对象,知道了一个widget
是怎样渲染在屏幕上显示,那下面就从main()方法入口,看看一个app从点击启动到运行的流程:
1.WidgetsFlutterBinding.ensureInitialized
//app入口
void main() => runApp(MyApp());
app入口函数就是调用了runApp方法,看看runApp方法里面做了什么:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
…attachRootWidget(app)
…scheduleWarmUpFrame();
}
首先参数(Widget app)是一个widget
,下面一行一行分析,进入**WidgetsFlutterBinding.ensureInitialized()**方法:
/// A concrete binding for applications based on the Widgets framework.
/// This is the glue that binds the framework to the Flutter engine.
//意思:基于widget framework的应用程序的具体绑定
//这是将framework widget和Flutter engine绑定的桥梁
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
/// Returns an instance of the [WidgetsBinding], creating and
/// initializing it if necessary. If one is created, it will be a
/// [WidgetsFlutterBinding]. If one was previously initialized, then
/// it will at least implement [WidgetsBinding].
///
/// You only need to call this method if you need the binding to be
/// initialized before calling [runApp].
///
/// In the flutter_test
framework, [testWidgets] initializes the
/// binding instance to a [TestWidgetsFlutterBinding], not a
/// [WidgetsFlutterBinding].
//上面注释意思是:返回[WidgetsBinding]的具体实例,必须创建和初始化,如果已//经创建了,那么它就是一个[WidgetsFlutterBinding],如果已经初始化了,那么//至少要实现[WidgetsBinding]
//如果你只想调用这个方法,那么你需要在调用runApp之前绑定并且初始化
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
看到这个WidgetsFlutterBinding
混入(with)很多的Binding
,下面先看父类BindingBase
:
abstract class BindingBase {
BindingBase() {
developer.Timeline.startSync(‘Framework initialization’);
assert(!_debugInitialized);
initInstances();
assert(_debugInitialized);
assert(!_debugServiceExtensionsRegistered);
initServiceExtensions();
assert(_debugServiceExtensionsRegistered);
developer.postEvent(‘Flutter.FrameworkInitialization’, <String, String>{});
developer.Timeline.finishSync();
}
static bool _debugInitialized = false;
static bool _debugServiceExtensionsRegistered = false;
ui.Window get window => ui.window;//获取window实例
@protected
@mustCallSuper
void initInstances() {
assert(!_debugInitialized);
assert(() { _debugInitialized = true; return true; }());
}
}
看到有句代码ui.Window get window => ui.window;,在Android中所有的视图都是通过window
来呈现的,那Flutter中也有window
,那看看window
在Flutter中的作用看看官方对它的定义:
意思是:链接宿主操作系统的接口,也就是Flutter framework 链接宿主操作系统的接口。系统中有一个Window实例,可以从window属性来获取,看看源码:
class Window {
Window._();
//返回DPI,DPI是每英寸的像素点数,是设备屏幕的固件属性
//获取可能不准确
double get devicePixelRatio => _devicePixelRatio;
double _devicePixelRatio = 1.0;
//绘制UI的区域大小
Size get physicalSize => _physicalSize;
Size _physicalSize = Size.zero;
//获取矩形的物理像素
WindowPadding get viewInsets => _viewInsets;
WindowPadding _viewInsets = WindowPadding.zero;
//获取内边距
WindowPadding get padding => _padding;
WindowPadding _padding = WindowPadding.zero;
//当绘制区域改变时触发
VoidCallback get onMetricsChanged => _onMetricsChanged;
VoidCallback _onMetricsChanged;
Zone _onMetricsChangedZone;
set onMetricsChanged(VoidCallback callback) {
_onMetricsChanged = callback;
_onMetricsChangedZone = Zone.current;
}
//当系统语言发生变化时触发回调
Locale get locale {
if (_locales != null && _locales.isNotEmpty) {
return _locales.first;
}
return null;
}
//获取系统语言
List get locales => _locales;
List _locales;
//当Local发生改变时触发回调
VoidCallback get onLocaleChanged => _onLocaleChanged;
VoidCallback _onLocaleChanged;
Zone _onLocaleChangedZone;
set onLocaleChanged(VoidCallback callback) {
_onLocaleChanged = callback;
_onLocaleChangedZone = Zone.current;
}
//当系统字体大小改变时触发回调
VoidCallback get onTextScaleFactorChanged => _onTextScaleFactorChanged;
VoidCallback _onTextScaleFactorChanged;
Zone _onTextScaleFactorChangedZone;
set onTextScaleFactorChanged(VoidCallback callback) {
_onTextScaleFactorChanged = callback;
_onTextScaleFactorChangedZone = Zone.current;
}
//屏幕亮度改变时触发回调
VoidCallback get onPlatformBrightnessChanged => _onPlatformBrightnessChanged;
VoidCallback _onPlatformBrightnessChanged;
Zone _onPlatformBrightnessChangedZone;
set onPlatformBrightnessChanged(VoidCallback callback) {
_onPlatformBrightnessChanged = callback;
_onPlatformBrightnessChangedZone = Zone.current;
}
//屏幕刷新时会回调
FrameCallback get onBeginFrame => _onBeginFrame;
FrameCallback _onBeginFrame;
Zone _onBeginFrameZone;
set onBeginFrame(FrameCallback callback) {
_onBeginFrame = callback;
_onBeginFrameZone = Zone.current;
}
//绘制屏幕时回调
VoidCallback get onDrawFrame => _onDrawFrame;
VoidCallback _onDrawFrame;
Zone _onDrawFrameZone;
set onDrawFrame(VoidCallback callback) {
_onDrawFrame = callback;
_onDrawFrameZone = Zone.current;
}
//点击或者指针事件触发回调
PointerDataPacketCallback get onPointerDataPacket => _onPointerDataPacket;
PointerDataPacketCallback _onPointerDataPacket;
Zone _onPointerDataPacketZone;
set onPointerDataPacket(PointerDataPacketCallback callback) {
_onPointerDataPacket = callback;
_onPointerDataPacketZone = Zone.current;
}
//获取请求的默认路由
String get defaultRouteName => _defaultRouteName();
String _defaultRouteName() native ‘Window_defaultRouteName’;
//该方法被调用后,onBeginFrame和onDrawFrame将紧连着会在合适时机调用
void scheduleFrame() native ‘Window_scheduleFrame’;
//更新应用在GPU上的渲染,这方法会直接调用Flutter engine的Window_render方法
void render(Scene scene) native ‘Window_render’;
//窗口的语义内容是否改变
bool get semanticsEnabled => _semanticsEnabled;
bool _semanticsEnabled = false;
//当窗口语言发生改变时回调
VoidCallback get onSemanticsEnabledChanged => _onSemanticsEnabledChanged;
VoidCallback _onSemanticsEnabledChanged;
Zone _onSemanticsEnabledChangedZone;
set onSemanticsEnabledChanged(VoidCallback callback) {
_onSemanticsEnabledChanged = callback;
_onSemanticsEnabledChangedZone = Zone.current;
}
//当用户表达写的动作时回调
SemanticsActionCallback get onSemanticsAction => _onSemanticsAction;
SemanticsActionCallback _onSemanticsAction;
Zone _onSemanticsActionZone;
set onSemanticsAction(SemanticsActionCallback callback) {
_onSemanticsAction = callback;
_onSemanticsActionZone = Zone.current;
}
//启用其他辅助功能回调
VoidCallback get onAccessibilityFeaturesChanged => _onAccessibilityFeaturesChanged;
VoidCallback _onAccessibilityFeaturesChanged;
Zone _onAccessibilityFlagsChangedZone;
set onAccessibilityFeaturesChanged(VoidCallback callback) {
_onAccessibilityFeaturesChanged = callback;
_onAccessibilityFlagsChangedZone = Zone.current;
}
//更新此窗口的语义数据
void updateSemantics(SemanticsUpdate update) native ‘Window_updateSemantics’;
//设置Isolate调试名称
void setIsolateDebugName(String name) native ‘Window_setIsolateDebugName’;
//向特定平台发送消息
void sendPlatformMessage(String name,
ByteData data,
PlatformMessageResponseCallback callback) {
final String error =
_sendPlatformMessage(name, _zonedPlatformMessageResponseCallback(callback), data);
if (error != null)
throw new Exception(error);
}
String _sendPlatformMessage(String name,
PlatformMessageResponseCallback callback,
ByteData data) native ‘Window_sendPlatformMessage’;
//获取平台消息的回调
PlatformMessageCallback get onPlatformMessage => _onPlatformMessage;
PlatformMessageCallback _onPlatformMessage;
Zone _onPlatformMessageZone;
set onPlatformMessage(PlatformMessageCallback callback) {
_onPlatformMessage = callback;
_onPlatformMessageZone = Zone.current;
}
//由_dispatchPlatformMessage调用
void _respondToPlatformMessage(int responseId, ByteData data)
native ‘Window_respondToPlatformMessage’;
//平台信息响应回调
static PlatformMessageResponseCallback _zonedPlatformMessageResponseCallback(PlatformMessageResponseCallback callback) {
if (callback == null)
return null;
// Store the zone in which the callback is being registered.
final Zone registrationZone = Zone.current;
return (ByteData data) {
registrationZone.runUnaryGuarded(callback, data);
};
}
}
可以知道window
包含当前设备系统的一些信息和回调,那么现在看看混入的各种Binding
:
- GestureBinding:绑定手势子系统,提供
onPointerDataPacket
回调。 - ServicesBinding:绑定平台消息通道,提供
onPlatformMessage
回调。 - SchedulerBinding:绑定绘制调度子系统,提供
onBeginFrame
和onDrawFrame
回调。 - PaintingBinding:绑定绘制库,处理图像缓存。
- SemanticsBinding:语义层和flutter engine的桥梁,对辅助功能的底层支持。
- RendererBinding:是渲染树和Flutter engine的桥梁,提供
onMetricsChanged
和onTextScaleFactorChanged
回调。 - WidgetsBinding:是Flutter widget和engine的桥梁,提供
onLocaleChanged
,onBuildSchedule
回调。
也就是WidgetsFlutterBinding.ensureInitialized()
这行代码看名字是将WidgetsFlutterBinding
实例初始化。其实并非那么简单,另外获取一些系统基本信息和初始化监听window
对象的一些事件,然后将这些事件按照上层Framework模型规则进行包装、抽象最后分发。简而言之WidgetsFlutterBinding
是Flutter engine
和Framework
的桥梁。
2.attachRootWidget(app)
第二行是attachRootWidget(app)
,看名字意思是将根RootWidget
挂载。点进去看:
//如果这个widget有必要创建,就将它附加到[renderViewElement]
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter(
container: renderView,
debugShortDescription: ‘[root]’,
child: rootWidget,
).attachToRenderTree(buildOwner, renderViewElement);
}
renderView
是UI渲染树的根节点:
// The render tree that’s attached to the output surface.
//挂载在渲染树 rootNode根节点
RenderView get renderView => _pipelineOwner.rootNode;
rootWidget
是外面所传进来的根Widget
,这里不用管它,下面看attachToRenderTree(buildOwner, renderViewElement)
这个方法,根据意思,这个方法应该构建RenderTree
,这个方法需要两个参数,首先看buildOwner
这个参数:
/// The [BuildOwner] in charge of executing the build pipeline for the
/// widget tree rooted at this binding.
BuildOwner get buildOwner => _buildOwner;
final BuildOwner _buildOwner = BuildOwner();
看官网对它的定义:
意思是:是widget framework的管理类,用来跟踪哪些widget需要重建,并处理widget树的其他任务,例如管理树的非活动元素列表,并在调试时在热重载期间在必要时触发“重组”命令,下面看另外一个参数renderViewElement
,代码注释如下:
/// The [Element] that is at the root of the hierarchy (and which wraps the
/// [RenderView] object at the root of the rendering hierarchy).
///
/// This is initialized the first time [runApp] is called.
Element get renderViewElement => _renderViewElement;
Element _renderViewElement;
renderViewElement
是renderView
对应的Element
对象,因为renderView
是树的根,所以renderViewElement
位于层次结构的根部,那下面点击attachToRenderTree
的源码看:
/// Inflate this widget and actually set the resulting [RenderObject] as the
/// child of [container].
///
/// If element
is null, this function will create a new element. Otherwise,
/// the given element will have an update scheduled to switch to this widget.
///
/// Used by [runApp] to bootstrap applications.
RenderObjectToWidgetElement attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement element ]) {
if (element == null) {
owner.lockState(() {
element = createElement();
assert(element != null);
element.assignOwner(owner);
});
owner.buildScope(element, () {
element.mount(null, null);
});
} else {
element._newWidget = this;
element.markNeedsBuild();
}
return element;
}
跟着代码往下走如果根element
没有创建,那么就调用createElement
创建根element
,element
和widget
进行关联。接着调用element.assignOwner(owner)
,这个方法其实就是设置这个element
的跟踪,最后调用owner.buildScope
这个方法,这个方法是确定更新widget
的范围。如果element
已经创建了,将根element
和关联的widget
设为新的,并且重新构建这个element
,为了后面的复用。
3.scheduleWarmUpFrame
runApp()方法最后一行执行scheduleWarmUpFrame
方法:
/// Schedule a frame to run as soon as possible, rather than waiting for
/// the engine to request a frame in response to a system “Vsync” signal.
///
/// This is used during application startup so that the first frame (which is
/// likely to be quite expensive) gets a few extra milliseconds to run.
///
/// Locks events dispatching until the scheduled frame has completed.
///
/// If a frame has already been scheduled with [scheduleFrame] or
/// [scheduleForcedFrame], this call may delay that frame.
///
/// If any scheduled frame has already begun or if another
/// [scheduleWarmUpFrame] was already called, this call will be ignored.
///
/// Prefer [scheduleFrame] to update the display in normal operation.
void scheduleWarmUpFrame() {
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle)
return;
_warmUpFrame = true;
Timeline.startSync(‘Warm-up frame’);
final bool hadScheduledFrame = _hasScheduledFrame;
// We use timers here to ensure that microtasks flush in between.
Timer.run(() {
assert(_warmUpFrame);
handleBeginFrame(null);—>1
});
Timer.run(() {
assert(_warmUpFrame);
handleDrawFrame(); ---->2
// We call resetEpoch after this frame so that, in the hot reload case,
// the very next frame pretends to have occurred immediately after this
// warm-up frame. The warm-up frame’s timestamp will typically be far in
// the past (the time of the last real frame), so if we didn’t reset the
// epoch we would see a sudden jump from the old time in the warm-up frame
// to the new time in the “real” frame. The biggest problem with this is
// that implicit animations end up being triggered at the old time and
// then skipping every frame and finishing in the new time.
resetEpoch();
_warmUpFrame = false;
if (hadScheduledFrame)
scheduleFrame();
});
// Lock events so touch events etc don’t insert themselves until the
// scheduled frame has finished.
lockEvents(() async {
await endOfFrame;
Timeline.finishSync();
});
}
首先这个方法在SchedulerBinding
里,这两个方法主要执行了handleBeginFrame
和handleDrawFrame
方法:
3.1.handleBeginFrame
void handleBeginFrame(Duration rawTimeStamp) {
…
try {
// TRANSIENT FRAME CALLBACKS
Timeline.startSync(‘Animate’, arguments: timelineWhitelistArguments);
_schedulerPhase = SchedulerPhase.transientCallbacks;
final Map<int, _FrameCallbackEntry> callbacks = _transientCallbacks;
_transientCallbacks = <int, _FrameCallbackEntry>{};
callbacks.forEach((int id, _FrameCallbackEntry callbackEntry) {
if (!_removedIds.contains(id))
_invokeFrameCallback(callbackEntry.callback, _currentFrameTimeStamp, callbackEntry.debugStack);
});
_removedIds.clear();
} finally {
_schedulerPhase = SchedulerPhase.midFrameMicrotasks;
}
}
可以看到主要是对transientCallbacks
队列操作,这个集合主要是放一些临时回调,存放动画回调。
3.2.handleDrawFrame
void handleDrawFrame() {
assert(_schedulerPhase == SchedulerPhase.midFrameMicrotasks);
Timeline.finishSync(); // end the “Animate” phase
try {
// PERSISTENT FRAME CALLBACKS ----->
_schedulerPhase = SchedulerPhase.persistentCallbacks;
for (FrameCallback callback in _persistentCallbacks)
_invokeFrameCallback(callback, _currentFrameTimeStamp);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/ebe54579ca38c4df0204d77f047ecb86.jpeg)
总结
作为一名从事Android的开发者,很多人最近都在和我吐槽Android是不是快要凉了?而在我看来这正是市场成熟的表现,所有的市场都是温水煮青蛙,永远会淘汰掉不愿意学习改变,安于现状的那批人,希望所有的人能在大浪淘沙中留下来,因为对于市场的逐渐成熟,平凡并不是我们唯一的答案!
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
g-W3dLGaEM-1712620622299)]
[外链图片转存中…(img-zSUK7gQH-1712620622300)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/ebe54579ca38c4df0204d77f047ecb86.jpeg)
总结
作为一名从事Android的开发者,很多人最近都在和我吐槽Android是不是快要凉了?而在我看来这正是市场成熟的表现,所有的市场都是温水煮青蛙,永远会淘汰掉不愿意学习改变,安于现状的那批人,希望所有的人能在大浪淘沙中留下来,因为对于市场的逐渐成熟,平凡并不是我们唯一的答案!
[外链图片转存中…(img-Ioc9lDYW-1712620622300)]
[外链图片转存中…(img-GNJHpY18-1712620622300)]
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!