用Flutter的开发的小伙伴知道,一般你想动态的修改小部件的的状态的话,那么你要实现小部件继承StatefulWidget,而不是StatelessWidget,这和ReactNative的方式如出一辙,你想改变组件的属性,只要调用SetState方法就可以了,至于SetState方法如何实现的,笔者接下来将会一一分解。
void setState(VoidCallback fn) {
if (_debugLifecycleState == _StateLifecycle.created && !mounted) {
throw FlutterError.fromParts(<DiagnosticsNode>[
ErrorSummary('setState() called in constructor: $this'),
ErrorHint(
'This happens when you call setState() on a State object for a widget that '
'hasn\'t been inserted into the widget tree yet. It is not necessary to call '
'setState() in the constructor, since the state is already assumed to be dirty '
'when it is initially created.'
),
]);
}
return true;
}());
//调用你设置部件属性的方法
final dynamic result = fn() as dynamic;
//调用element的markNeedsBuild方法来将当前的元素加入脏数据,以备后续的处理
_element.markNeedsBuild();
}
这个方法很简单,一是调用你设置属性的方法,二是调用StatefulElement的markNeedsBuild方法,标记这个元素会被用来重新构建RenderObject渲染树
void markNeedsBuild() {
//当前元素不活跃的话将直接返回
if (!_active)
return;
//如果已经设置完_dirty则返回,让SetState方法串行执行
if (dirty)
return;
_dirty = true;
//调用BuildOwner的scheduleBuildFor方法将当前元素加入脏数据中
owner.scheduleBuildFor(this);
}
void scheduleBuildFor(Element element) {
//如果已经在脏数据集合中不需要重新添加
if (element._inDirtyList) {
_dirtyElementsNeedsResorting = true;
return;
}
if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
_scheduledFlushDirtyElements = true;
//确保已经注册了垂直同步信号
onBuildScheduled();
}
//将当前要改变的元素加入到脏数据_dirtyElements中
_dirtyElements.add(element);
//标记元素已经放入脏数据集合中
element._inDirtyList = true;
}
看到这可以看出SetState方法就是将当前的element放入脏数据集合,而后标记它为脏元素,此次setState方法就处理完了,那么
是谁来处理的这个脏数据集合呢?这里的脏数据集合其实就是用来记录当前哪些元素需要重新构建RenderObject的属性,从而重新实现哪些RenderObject需要重新渲染。通俗的讲就是每当硬件层的垂直同步信号到来将重新实现View树的测量、布局、重绘,然后将数据交给Gpu渲染的屏幕上,也就是说每过约16.6666666....ms,脏数据元素会被处理一次,然后清空。
那么垂直同步信号是什么时候被注册的,Flutter sdk中的Dart部分最重要的就是binding类,而实现监听同步信号的Binding类为SchedulerBinding这个类,Flutter的UI渲染一篇中已经讲过Binding创建完之后,都会执行initInstances的方法,而最终注册的方法为initInstances-》readInitialLifecycleStateFromNativeWindow-》handleAppLifecycleStateChanged-》scheduleFrame
void scheduleFrame() {
if (_hasScheduledFrame || !_framesEnabled)
return;
assert(() {
if (debugPrintScheduleFrameStacks)
debugPrintStack(label: 'scheduleFrame() called. Current phase is $schedulerPhase.');
return true;
}());
//为window垂直同步信号返回绘制的方法注册回调方法
ensureFrameCallbacksRegistered();
window.scheduleFrame();
_hasScheduledFrame = true;
}
void scheduleFrame() native 'Window_scheduleFrame';
scheduleFrame最终会调用底层引擎的Window_scheduleFrame方法,Window_scheduleFrame最终会调用java层的
flutter/shell/platform/android/io/flutter/view/VsyncWaiter.java的方法AsyncWaitForVsyncDelegate方法
private final FlutterJNI.AsyncWaitForVsyncDelegate asyncWaitForVsyncDelegate =
new FlutterJNI.AsyncWaitForVsyncDelegate() {
@Override
public void asyncWaitForVsync(long cookie) {
Choreographer.getInstance()
.postFrameCallback(
new Choreographer.FrameCallback() {
@Override
public void doFrame(long frameTimeNanos) {
float fps = windowManager.getDefaultDisplay().getRefreshRate();
long refreshPeriodNanos = (long) (1000000000.0 / fps);
FlutterJNI.nativeOnVsync(
frameTimeNanos, frameTimeNanos + refreshPeriodNanos, cookie);
}
});
}
};
可以看出,Flutter在android中最终通过Choreographer注册了垂直时钟信号的监听方法,而注册了垂直信号之后,每过16.6毫秒最终会调用到Dart层的
void ensureFrameCallbacksRegistered() {
//给垂直同步信号注册回调方法
window.onBeginFrame ??= _handleBeginFrame;
window.onDrawFrame ??= _handleDrawFrame;
}
onBeginFrame方法和onDrawFrame方法,先执行onBeginFrame再 执行onDrawFrame,这么一来就实现了,每次垂直同步信号的到来都会引起Flutter小部件树的部分重新的渲染,因此达到了动态的效果,其实动画(动画的回调在onBeginFrame执行)也是基于垂直同步信号的原理。
那么最后再来看一下渲染是怎么处理脏数据的
void drawFrame() {
if (_needToReportFirstFrame && _reportFirstFrame) {
assert(!_firstFrameCompleter.isCompleted);
TimingsCallback firstFrameCallback;
firstFrameCallback = (List<FrameTiming> timings) {
if (!kReleaseMode) {
developer.Timeline.instantSync('Rasterized first useful frame');
developer.postEvent('Flutter.FirstFrame', <String, dynamic>{});
}
SchedulerBinding.instance.removeTimingsCallback(firstFrameCallback);
_firstFrameCompleter.complete();
};
//添加时钟回调接口
SchedulerBinding.instance.addTimingsCallback(firstFrameCallback);
}
try {
if (renderViewElement != null)
//重新构造脏数据的View树
buildOwner.buildScope(renderViewElement);
super.drawFrame();
buildOwner.finalizeTree();
} finally {
assert(() {
debugBuildingDirtyElements = false;
return true;
}());
}
if (!kReleaseMode) {
if (_needToReportFirstFrame && _reportFirstFrame) {
developer.Timeline.instantSync('Widgets built first useful frame');
}
}
_needToReportFirstFrame = false;
}
这个方法里面最重要的就是 buildOwner.buildScope(开始根据脏数据元素重新构建渲染树),super.drawFrame重新测量布局和渲染
void buildScope(Element context, [ VoidCallback callback ]) {
if (callback == null && _dirtyElements.isEmpty)
return;
try {
_scheduledFlushDirtyElements = true;
if (callback != null) {
assert(_debugStateLocked);
Element debugPreviousBuildTarget;
_dirtyElementsNeedsResorting = false;
try {
//将根元素下的所有子元素都生成Elment元素
callback();
} finally {
}
}
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
int dirtyCount = _dirtyElements.length;
int index = 0;
while (index < dirtyCount) {
//脏数据树的重新构建
try {
_dirtyElements[index].rebuild();
} catch (e, stack) {
);
}
index += 1;
if (dirtyCount < _dirtyElements.length || _dirtyElementsNeedsResorting) {
_dirtyElements.sort(Element._sort);
_dirtyElementsNeedsResorting = false;
dirtyCount = _dirtyElements.length;
while (index > 0 && _dirtyElements[index - 1].dirty) {
index -= 1;
}
}
}
} finally {
//清空脏数据集合
for (Element element in _dirtyElements) {
assert(element._inDirtyList);
element._inDirtyList = false;
}
_dirtyElements.clear();
_scheduledFlushDirtyElements = false;
_dirtyElementsNeedsResorting = null;
Timeline.finishSync();
}
buildScope重新实现了脏数据元素的重构方法rebuild,也就是从当前的节点的元素开始构建子元素以及子元素的RenderObject的创建,当然子元素并不一定重新创建,这需要和旧的元素去做比较。
重新构建渲染树之后就是真正的渲染的开始了。如下
void drawFrame() {
assert(renderView != null);
//确定布局的位置
pipelineOwner.flushLayout();
pipelineOwner.flushCompositingBits();
//开始执行画画
pipelineOwner.flushPaint();
//合并图层将绘制的数据发送给Gpu渲染
renderView.compositeFrame(); // this sends the bits to the GPU
pipelineOwner.flushSemantics(); // this also sends the semantics to the OS.
}