Flutter Provider原理深入浅出(中)

上文,我们大致分析了ChangeNotifierProviderConsumer的源码,但由于很多回调都是系统触发的,导致整个渲染的原理我们还没有盘清楚,带着之前遗留的问题,我们通过调试代码,来试图解开这些秘密。

1 我们在使用ChangeNotifierProvider的时候,最终是包装成_Delegate的子类_CreateInheritedProvider,然而createElement是谁调的
2 分析Consumer,最终在调用builder的时候使用到了系统回调contextcontext的类型为什么是SingleChildStatelessElement
3 在给builder传递参数的时候,使用的Provider.of<T>(context)最终是使用Element类的_inheritedWidgets存储的InheritedElement,最终应用层拿到的T实际是inheritedElement.value,那InheritedElementT又是如何建立关系的

第一个问题:createElement,通过断点代码

_InheritedProviderScope.createElement (inherited_provider.dart:310)
Element.inflateWidget (framework.dart:3564)
Element.updateChild (framework.dart:3327)
ComponentElement.performRebuild (framework.dart:4652)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)
SingleChildWidgetElementMixin.mount (nested.dart:223)
--------------------------------------------------------------------------------------------------
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
ComponentElement.performRebuild (framework.dart:4652)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)
_NestedHookElement.mount (nested.dart:188)
--------------------------------------------------------------------------------------------------
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
ComponentElement.performRebuild (framework.dart:4652)
_InheritedProviderScopeElement.performRebuild (inherited_provider.dart:426)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)
--------------------------------------------------------------------------------------------------
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
ComponentElement.performRebuild (framework.dart:4652)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)
SingleChildWidgetElementMixin.mount (nested.dart:223)
--------------------------------------------------------------------------------------------------
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
ComponentElement.performRebuild (framework.dart:4652)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)
_NestedHookElement.mount (nested.dart:188)
--------------------------------------------------------------------------------------------------
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
ComponentElement.performRebuild (framework.dart:4652)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)
--------------------------------------------------------------------------------------------------
SingleChildWidgetElementMixin.mount (nested.dart:223)
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
RenderObjectToWidgetElement._rebuild (binding.dart:1252)
RenderObjectToWidgetElement.mount (binding.dart:1223)
--------------------------------------------------------------------------------------------------
RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (binding.dart:1165)
BuildOwner.buildScope (framework.dart:2683)
--------------------------------------------------------------------------------------------------
RenderObjectToWidgetAdapter.attachToRenderTree (binding.dart:1164)
WidgetsBinding.attachRootWidget (binding.dart:974)
WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (binding.dart:955)
_rootRun (zone.dart:1182)
_CustomZone.run (zone.dart:1093)
_CustomZone.runGuarded (zone.dart:997)
_CustomZone.bindCallbackGuarded.<anonymous closure> (zone.dart:1037)
_rootRun (zone.dart:1190)
_CustomZone.run (zone.dart:1093)
_CustomZone.bindCallback.<anonymous closure> (zone.dart:1021)
Timer._createTimer.<anonymous closure> (timer_patch.dart:18)
_Timer._runTimers (timer_impl.dart:397)
_Timer._handleMessage (timer_impl.dart:428)
_RawReceivePortImpl._handleMessage (isolate_patch.dart:168)

我们看到,堆栈非常长,我们先从上往下看,inflateWidget主要作用应该是将布局文件实例化为View,ComponentElement调用mount时,最终调用到了ElementinflateWidget

从下往上看,是应用启动的流程

main.dart
void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..attachRootWidget(app)
    ..scheduleWarmUpFrame(); 
}

widgets/binding.dart
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {

  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding(); 
    return WidgetsBinding.instance;
  }
}

我们写一个app,入口都会是main函数,在Flutter App中main函数会调用runApp()方法,这里首先会调用ensureInitialized来初始化WidgetsBinding单例对象,WidgetsFlutterBinding继承BindingBase抽象类,并且附带7个mixin,会先调用父类的构造函数

  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();
  }

BindingBase构造的时候调用initInstancesinitServiceExtensions完成以下的工作:

  • 执行initInstances()方法会依次调用到每一个mixin来初始化一些状态;
  • 执行initServiceExtensions方法会依次调用WidgetsBinding,RendererBinding,SchedulerBinding,ServicesBinding,BindingBase这5个类,主要是根据不同的包(release/profile/debug)调用registerServiceExtension()方法来注册各种扩展服务用于debug。
mixin WidgetsBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;

    assert(() {
      _debugAddStackFilters();
      return true;
    }());

    // Initialization of [_buildOwner] has to be done after
    // [super.initInstances] is called, as it requires [ServicesBinding] to
    // properly setup the [defaultBinaryMessenger] instance.
    _buildOwner = BuildOwner();
    buildOwner.onBuildScheduled = _handleBuildScheduled;
    window.onLocaleChanged = handleLocaleChanged;
    window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
    SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
    FlutterErrorDetails.propertiesTransformers.add(transformDebugCreator);
  }

WidgetsBinding初始化过程会设置window的如下回调方法:

  • onLocaleChanged
  • onAccessibilityFeaturesChanged

其它几种Binding暂时不展开说,继续围绕WidgetsBinding来深入分析,ensureInitialized拿到了WidgetsBinding的单例对象,调用attachRootWidget

void scheduleAttachRootWidget(Widget rootWidget) {
    Timer.run(() {
      attachRootWidget(rootWidget);
    });
  }

scheduleAttachRootWidget添加异步任务attachRootWidgetTimer.run官方给的解释是尽快执行一个异步任务,其实就是把attachRootWidget这个任务异步来执行,不影响当前程序继续执行

 /**
   * Runs the given [callback] asynchronously as soon as possible.
   *
   * This function is equivalent to `new Timer(Duration.zero, callback)`.
   */
  static void run(void Function() callback) {
    new Timer(Duration.zero, callback);
  }
/// Takes a widget and attaches it to the [renderViewElement], creating it if
  /// necessary.
  ///
  /// This is called by [runApp] to configure the widget tree.
  ///
  /// See also:
  ///
  ///  * [RenderObjectToWidgetAdapter.attachToRenderTree], which inflates a
  ///    widget and attaches it to the render tree.
  void attachRootWidget(Widget rootWidget) {
    _readyToProduceFrames = true;
    _renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
      container: renderView,
      debugShortDescription: '[root]',
      child: rootWidget,
    ).attachToRenderTree(buildOwner, renderViewElement as RenderObjectToWidgetElement<RenderBox>);
  }

来到了attachRootWidget的实现,通过RenderObjectToWidgetAdapter将传入的Widget转成Element,至此我们知道,WidgetElement建立了关系,我们再详细分析下renderViewRenderObjectToWidgetAdapter这个类

rendering/binding.dart

mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
  @override
  void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
  }

  void initRenderView() {
    assert(!_debugIsRenderViewInitialized);
    assert(() {
      _debugIsRenderViewInitialized = true;
      return true;
    }());
    renderView = RenderView(configuration: createViewConfiguration(), window: window);
    renderView.prepareInitialFrame();
  }

renderView是在RendererBinding初始化时候创建的,并将renderView存储在_pipelineOwner.rootNode,代码如下:

set renderView(RenderView value) {
    assert(value != null);
    _pipelineOwner.rootNode = value;
  }

再来看RenderObjectToWidgetAdapter类,这个类非常重要,它建立起了WidgetElement的关联

class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
  /// Creates a bridge from a [RenderObject] to an [Element] tree.
  ///
  /// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
  RenderObjectToWidgetAdapter({
    this.child,
    this.container,
    this.debugShortDescription,
  }) : super(key: GlobalObjectKey(container));

  @override
  RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);

  RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner, [ RenderObjectToWidgetElement<T> element ]) {
    if (element == null) {
      owner.lockState(() {
        element = createElement();
        assert(element != null);
        element.assignOwner(owner);
      });
      owner.buildScope(element, () {
        element.mount(null, null);
      });
      // This is most likely the first time the framework is ready to produce
      // a frame. Ensure that we are asked for one.
      SchedulerBinding.instance.ensureVisualUpdate();
    } else {
      element._newWidget = this;
      element.markNeedsBuild();
    }
    return element;
  }

RenderObjectWidget的子类,构造函数有三个参数childcontainerdebugShortDescriptionattachToRenderTree里如果element没有创建,就创建并返回它,如果创建了,就会使用已经创建的element\color{#FF0000}{注意了},在创建element的时候createElement调用了,我们本文开始设置的问题1终于有了答案,createElementRenderObjectToWidgetAdapter调用的,发生在创建element的时候,而调用createElement的时候,将this也就是RenderObjectToWidgetAdapter对象作为参数层层传递,最终给了Element,而this本身存储了Widget,存在了Elementwidget成员变量中

abstract class Element extends DiagnosticableTree implements BuildContext {
  /// Creates an element that uses the given widget as its configuration.
  ///
  /// Typically called by an override of [Widget.createElement].
  Element(Widget widget)
    : assert(widget != null),
      _widget = widget;

  Element _parent;

最终传递给抽象类Element构造函数中的widget也是RenderObjectToWidgetAdapter类的对象,element第一次创建后会调用element.mount(null, null); 并出现这些堆栈

SingleChildWidgetElementMixin.mount (nested.dart:223)
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
RenderObjectToWidgetElement._rebuild (binding.dart:1252)
RenderObjectToWidgetElement.mount (binding.dart:1223)
--------------------------------------------------------------------------------------------------
RenderObjectToWidgetAdapter.attachToRenderTree.<anonymous closure> (binding.dart:1165)
BuildOwner.buildScope (framework.dart:2683)
--------------------------------------------------------------------------------------------------
RenderObjectToWidgetAdapter.attachToRenderTree (binding.dart:1164)
WidgetsBinding.attachRootWidget (binding.dart:974)
WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (binding.dart:955)
_rootRun (zone.dart:1182)
_CustomZone.run (zone.dart:1093)
_CustomZone.runGuarded (zone.dart:997)
_CustomZone.bindCallbackGuarded.<anonymous closure> (zone.dart:1037)
_rootRun (zone.dart:1190)
_CustomZone.run (zone.dart:1093)
_CustomZone.bindCallback.<anonymous closure> (zone.dart:1021)
Timer._createTimer.<anonymous closure> (timer_patch.dart:18)
_Timer._runTimers (timer_impl.dart:397)
_Timer._handleMessage (timer_impl.dart:428)
_RawReceivePortImpl._handleMessage (isolate_patch.dart:168)

继续看inflateWidget

framework.dart

Element inflateWidget(Widget newWidget, dynamic newSlot) {
    assert(newWidget != null);
    final Key key = newWidget.key;
    if (key is GlobalKey) {
      final Element newChild = _retakeInactiveElement(key, newWidget);
      if (newChild != null) {
        assert(newChild._parent == null);
        assert(() {
          _debugCheckForCycles(newChild);
          return true;
        }());
        newChild._activateWithParent(this, newSlot);
        final Element updatedChild = updateChild(newChild, newWidget, newSlot);
        assert(newChild == updatedChild);
        return updatedChild;
      }
    }
    final Element newChild = newWidget.createElement();
    assert(() {
      _debugCheckForCycles(newChild);
      return true;
    }());
    newChild.mount(this, newSlot);
    assert(newChild._debugLifecycleState == _ElementLifecycle.active);
    return newChild;
  }
  

inflateWidget是抽象类Element的方法,这里的参数newWidgetRenderObjectToWidgetElement.mount层层传递进去的,就是存储在RenderObjectToWidgetAdapter中的child,也就是我们在runApp时候传递的rootWidget

随着inflateWidget的执行,newWidget.createElement调用,如果newWidgetStatefulWidget就会执行StatefulElement(this);

abstract class StatefulWidget extends Widget {
  /// Initializes [key] for subclasses.
  const StatefulWidget({ Key key }) : super(key: key);

  /// Creates a [StatefulElement] to manage this widget's location in the tree.
  ///
  /// It is uncommon for subclasses to override this method.
  @override
  StatefulElement createElement() => StatefulElement(this);

如果widgetInheritedProvider,那就会执行InheritedProvidercreateElement,如果是_InheritedProviderScope,就会执行_InheritedProviderScopecreateElement。当调用newWidget.createElement时,会产生多态。

至此,我们已经清楚了,

final Element newChild = newWidget.createElement();

createElement可以创建任何Element的子类对象,下面是调用newChild.mount,由于newChild的类型不同,mount调用也是Element子类或者说实现类的mount方法,这里也解释了,mount方法就是这个时候调用的。我们通常使用的mounted的定义如下

abstract class State<T extends StatefulWidget> with Diagnosticable {
  bool get mounted => _element != null;
}

这里也层面的解释了一个问题,是现有Element对象还是先调用mount方法,答案是:先有Element对象后调用的mount方法,当我们一个widget被加到Element tree后,mounted就应该是true了

newChild.mount(this, newSlot);

image.png

如果newChild_InheritedProviderScopeElement类型的,会有下面的堆栈:

_CreateInheritedProvider.createState (inherited_provider.dart:605)
_InheritedProviderScopeElement.performRebuild (inherited_provider.dart:424)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)
Element.inflateWidget (framework.dart:3569)
Element.updateChild (framework.dart:3327)
ComponentElement.performRebuild (framework.dart:4652)
Element.rebuild (framework.dart:4343)
ComponentElement._firstBuild (framework.dart:4606)
ComponentElement.mount (framework.dart:4601)

这才调用到上文提到的createState是在_InheritedProviderScopeElement类中performRebuild方法执行时候调用的,代码如下

  void performRebuild() {
    if (_firstBuild) {
      _firstBuild = false;
      _delegateState = widget.owner._delegate.createState()..element = this;
    }
    super.performRebuild();
  }

这里的widget.owner._delegate其实就是_Delegate类的子类_CreateInheritedProvider的实例,上文已经解释 ,这里不做过多说明,那随着createState的调用_CreateInheritedProviderState的对象应运而生,后面就由_delegateState进行表演了。

结语

本文所分析的都是初始化的流程,并结合上文遗留的问题进行讲解,主要通过启动流程,来搞清楚WidgetElement是如何建立关系的,接下来,我们需要聚焦的问题是:

  • 当数据发生改变的时候,视图是如何发生变化的(Provider的原理)
  • 我们通常使用的StatefulWidget视图刷新机制和Provider有什么不同
  • initState, setState的底层原理是什么
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 219,928评论 6 509
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,748评论 3 396
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 166,282评论 0 357
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,065评论 1 295
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,101评论 6 395
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,855评论 1 308
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,521评论 3 420
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,414评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,931评论 1 319
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,053评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,191评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,873评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,529评论 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,074评论 0 23
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,188评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,491评论 3 375
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,173评论 2 357

推荐阅读更多精彩内容