美文网首页
Flutter Provider原理深入浅出(中)

Flutter Provider原理深入浅出(中)

作者: bigParis | 来源:发表于2021-01-08 19:00 被阅读0次

    上文,我们大致分析了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的底层原理是什么

    相关文章

      网友评论

          本文标题:Flutter Provider原理深入浅出(中)

          本文链接:https://www.haomeiwen.com/subject/uednoktx.html