Flutter状态管理Provider(二) 源码分析

2,697 阅读7分钟

接前一篇文章 Flutter状态管理Provider(一) 简易使用

过程分析

1.State之setState

我们debug前一篇示例的代码。

///----------------->
///进入setState
 void setState(VoidCallback fn) {
   final Object? result = fn() as dynamic;
   _element!.markNeedsBuild();///这一步Element
 }
///----------------->
///进入Element
  /// Marks the element as dirty and adds it to the global list of widgets to
  /// rebuild in the next frame.
  ///将element标记位dirty,并加入到全局列表,用于下一帧rebuild
  ///
  /// Since it is inefficient to build an element twice in one frame,
  /// applications and widgets should be structured so as to only mark
  /// widgets dirty during event handlers before the frame begins, not during
  /// the build itself.最好仅在帧开始之前标记,非build过程中
  //
void markNeedsBuild() {
    if (!_active) return;
    if (dirty) return;
    _dirty = true;
    owner.scheduleBuildFor(this); ///BuildOwner
 }
///----------------->
///BuildOwner.scheduleBuildFor:
 /// Adds an element to the dirty elements list.so that it will be rebuilt  
 /// when [WidgetsBinding.drawFrame] calls [buildScope].
 ///把element加入到dirty列表,这样drawFrame时就能rebuild 这个 dirty element
  void scheduleBuildFor(Element element) {
    if (element._inDirtyList) {
        _dirtyElementsNeedsResorting = true;
      return;
    };
    if (!_scheduledFlushDirtyElements && onBuildScheduled != null) {
      _scheduledFlushDirtyElements = true;
      onBuildScheduled();
    }
    _dirtyElements.add(element);
    element._inDirtyList = true;
  }

重新构建的调用栈是这样的。

首先:告诉系统需要重新构建

onBuildScheduled() 最终调用 SchedulerBinding.scheduleFrame().

告诉系统,等下次合适的机会,调用PlatformDispatcher._drawFrame来重新构建。

那这个机会什么时候来?在Android上就是等Choreographer.FrameCallback.doFrame,即Vsync信号.

然后:Vsync到来后的重新构建

PlatformDispatcher._drawFrame最终会调用我们XXWidgetState.build()

更多细节可以参考

Flutter的setState更新原理和流程 

Flutter 核心渲染流程分析

2.InheritedWidget

setState 通过 Element.markNeedsBuild(),让系统在合适的机会rebuild。

接下来是InheritedWidget,关注两个问题:

  • 如何共享数据?
  • 如何控制rebuild?

2.1.如何共享数据

首先要从InheritedWidget的使用入手

////CountProvider继承InheritedWidget,保存我们的数据
class CountProvider extends InheritedWidget {
  final int count;
  ...
}
//拿到CountProvider实例
CountProvider provider1 = BuildContext.getElementForInheritedWidgetOfExactType<CountProvider>().widget;
var count=provider1.count;

先来看BuildContext对象

/// [BuildContext] objects are actually [Element] objects. The [BuildContext]
/// interface is used to discourage direct manipulation of [Element] objects.
///BuildContext对象 其实就是Element对象。BuildContext接口是用来阻止对Element对象的直接访问。显而易见,Element是实现细节,系统不希望我们直接操作Element
abstract class BuildContext {
    Widget get widget;
    BuildOwner? get owner;
    RenderObject? findRenderObject();
    InheritedElement? getElementForInheritedWidgetOfExactType<T extends InheritedWidget>();
    T? dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object? aspect });
    InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect });
}

InheritedElement是啥?我们进入到InheritedWidget。

abstract class InheritedWidget extends ProxyWidget {
  const InheritedWidget({ Key key, Widget child })
    : super(key: key, child: child);

  ///对应Element是InheritedElement
  @override
  InheritedElement createElement() => InheritedElement(this);

 ///当InheritedWidget rebuilt时,不一定要rebuilt依赖它的widget,这取决于你的updateShouldNotify方法。
  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

我继续回到BuildContext.getElementForInheritedWidgetOfExactTypeElement

abstract class Element implements BuildContext {
 
 Map<Type, InheritedElement> _inheritedWidgets;
  
 @override
 InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
 ///key为T的Type,从_inheritedWidgets中获取 InheritedElement
   final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
   return ancestor;
 }
}
///子Element共享其parent的_inheritedWidgets。这个很关键。
void _updateInheritance() {
...
   _inheritedWidgets = _parent?._inheritedWidgets;
 }
 
 ///_updateInheritance()是在mount()或者active()中调用 
 ///mount()是首次把element添加到element Tree,然后 element是active状态
 ///active()是把deactivate element ,重新标记为active状态
 ///总之是从无到有或者从不可见到可见。
 @mustCallSuper
 void mount(Element parent, dynamic newSlot) {
 ...
   _active = true;
   _updateInheritance();
 }
 
 @mustCallSuper
 void activate() {
 ...
   _dependencies?.clear();
   _updateInheritance();
 }

InheritedElement 继承自Element。那它的_updateInheritance方法是怎样的? 把自身加了进去。这样:

  • 下一层Element可以拿到上面的InheritedWidget
  • 但上一层Element无法拿到下面的InheritedWidget 回过头来看,其实也挺简单的。当前Element会继承(保存)parent的InheritedElement。所以不管Element 与InheritedElement相隔多少层,只要是childElement,都是可以传递过来的。
class InheritedElement{
    @override
  void _updateInheritance() {
    assert(_active);
    final Map<Type, InheritedElement> incomingWidgets = _parent?._inheritedWidgets;
    if (incomingWidgets != null)
      _inheritedWidgets = HashMap<Type, InheritedElement>.from(incomingWidgets);
    else
      _inheritedWidgets = HashMap<Type, InheritedElement>();
    _inheritedWidgets[widget.runtimeType] = this;
  }
}

2.2.如何控制rebuild

2.2.1 Element与InheritedElement互相引用

现在我们进入到Element.dependOnInheritedWidgetOfExactType

abstract class Element implements BuildContext {
 @override
 T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
   final InheritedElement ancestor =  _inheritedWidgets[T];
   ///建立依赖,双向引用
   return dependOnInheritedElement(ancestor, aspect: aspect);
 }
 
 @override
 InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
   _dependencies ??= HashSet<InheritedElement>();
   ///Element与InheritedElement互相引用
   _dependencies.add(ancestor);
   ancestor.updateDependencies(this, aspect);
   return ancestor.widget;
 }
}
class InheritedElement{
   @protected
   void updateDependencies(Element dependent, Object aspect) {
       setDependencies(dependent, null);
   } 
   @protected
   void setDependencies(Element dependent, Object value) {
       _dependents[dependent] = value;
   }
}
2.2.2 Element.rebuild执行过程

setState()后会执行Element.rebuild(),继而调用performRebuild(基类抽象方法,需要子类实现)。继而来看我们关心的Element的子类

abstract class Element implements BuildContext {
  void rebuild() {
    ...
    performRebuild();
    ...
  }
  ///基类抽象方法,需要子类实现。
 @protected
 void performRebuild();
}
classDiagram
Element <|-- ComponentElement
ComponentElement <|-- StatefulElement
ComponentElement <|-- ProxyElement
ProxyElement <|-- InheritedElement

我们以上篇自定义的ChangeNotifierProvider为例来讲InheritedElement。

class ChangeNotifierProvider extends StatefulWidget {
  final Widget child;
  final CountModel model;
  ...
}
class _ChangeNotifierProviderState extends State<ChangeNotifierProvider> {
  ..
  @override
  Widget build(BuildContext context) {
    return CountProvider( model: widget.model, child: widget.child);
  }
}

class CountProvider extends InheritedWidget {
   final CountModel model;
    ...
   static CountModel of(BuildContext context, bool depend) {
       var provider = depend
          ? context.dependOnInheritedWidgetOfExactType<CountProvider>()
          : context.getElementForInheritedWidgetOfExactType<CountProvider>()?.widget;
      return (provider as CountProvider).model;
   }
  @override
  bool updateShouldNotify(CountProvider old) { return true;}
}

我们进入ComponentElement

///此时的ComponentElement与ChangeNotifierProvider对应
abstract class ComponentElement extends Element{
  @override
  void performRebuild() {
  ///调用_ChangeNotifierProviderState.build(),built为新生成的CountProvider
    Widget built= build();
    _child = updateChild(_child, built, slot);
  }
}

  
  @protected
  Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
    ///newWidget为null,则让child失活不可见,并不会清除。
    if (newWidget == null) {
      if (child != null)
        deactivateChild(child);
      return null;
    }
    if (child != null) {
      ///新老CountProvider是同一个对象,处理下slot,及可见性
      ///这个地方也告诉我们CountProvider的child每次都new一个对象,也会全部rebuilt
      if (child.widget == newWidget) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        return child;
      }
      if (Widget.canUpdate(child.widget, newWidget)) {
        if (child.slot != newSlot)
          updateSlotForChild(child, newSlot);
        ///此时 child是InheritedElement,newWidget是我们新构建CountProvider
        ///*只有这里才会进入我们的InheritedElement
        child.update(newWidget);
        return child;
      }
      deactivateChild(child);
    }
    return inflateWidget(newWidget, newSlot);
  }

接着上述的child.update(newWidget)InheritedElement结合ProxyElement一起看。

class ProxyElement extends ComponentElement{
    @override
    void update(ProxyWidget newWidget) {
        final ProxyWidget oldWidget = widget;
        super.update(newWidget);
        updated(oldWidget);
        _dirty = true;
        rebuild();
    }
    @protected
    void updated(covariant ProxyWidget oldWidget) {
        notifyClients(oldWidget);
  }
}

InheritedElement重载了updated()方法,额外增加updateShouldNotify判断,减少不必要重建。同时实现了notifyClients(),通知给引用InheritedElement的Element。

class InheritedElement extends ProxyElement{
    @override
    void updated(InheritedWidget oldWidget) {
    if (widget.updateShouldNotify(oldWidget))
      super.updated(oldWidget);
    }
    @override
    void notifyClients(InheritedWidget oldWidget) {
        ///_dependents中保存的都是
        ///通过dependOnInheritedWidgetOfExactType产生依赖的子孙Element
        for (Element dependent in _dependents.keys) {
            notifyDependent(oldWidget, dependent);
        }
    }
    @protected
    void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
        ///最终调用dependent的markNeedsBuild();
        dependent.didChangeDependencies();
    }
}

abstract Element{
  @mustCallSuper
  void didChangeDependencies() {
    ...
    markNeedsBuild();
  }
}

到这里我们算是把方法的调用栈说明白了。也对InheritedWidget的内部原理大概有了认识。 其实就是观察者模式:注册监听,通知更新的逻辑。还有更多细节还在继续学习中。

这些内容,足够让我们进入到Provider的使用过程中。

3.ChangeNotifierProvider

这个地方我们挑常用的ChangeNotifierProvider分析 回顾代码

class CountModel extends ChangeNotifier {
 int count;
 void increment() {
   count++;
   notifyListeners();
 }
}

class _ProviderDemoWidget4State extends State<ProviderDemoWidget4> {
 CountModel _countModel = CountModel(0);

 @override
 Widget build(BuildContext context) {
   return Scaffold(
    ...
     body: ChangeNotifierProvider.value(
       value: _countModel,
       ...
         child: Column(
           children: <Widget>[
             Consumer<CountModel>(
               builder: (contextC, model, child) {
                 return Text("计数:${model.count}(有依赖情况)");
               },
             ),
             Builder(builder: (context2) {
               return Text( "计数:${Provider.of<CountModel>(context2, listen: false).count}(无依赖情况)");
             }),
             RaisedButton(child: Text("increment"),onPressed: () => _countModel.increment()),
           ],
         ),
     ),
   );
 }
}

InheritedProvider类图

ChangeNotifierProvider最终继承InheritedProvider。InheritedProvider是整个Provider的中心。它的关键组件: _ValueInheritedProvider,_InheritedProviderScope.

_Delegate(_ValueInheritedProvider)与 _DelegateState

InheritedProvider将以下功能委托给_ValueInheritedProvider:

  • 数据T赋值,
  • 监听设置,
  • shouldUpdate
_InheritedProviderScope 与 _InheritedProviderScopeElement
  • _InheritedProviderScopeElement继承InteritedElement
  • _InheritedProviderScope 继承 InheritedWidget

ChangeNotifierProvider初始化过程

通过前一个篇章的分析,我们知道ComponentElement的初始/重新构建是在performBuild()中. _InheritedProviderScopeElement也不例外,其中的逻辑大抵是与InheritedElement相近的。

接着看子Element的构建,发现model的获取有两种方式Consumer和Provider。

    Consumer<CountModel>(
        builder: (contextC, model, child) {
            return Text("计数:${model.count}(有依赖情况)");
        },),
    Builder(builder: (context2) {
        return Text(
            "计数:${Provider.of<CountModel>(context2, listen: false).count}(无依赖情况)");
        }),

Consumer里面代码相对简单些,可以理解成把Provider.of(context)封装了了,默认listen为true。Consumer即消费,那肯定是要添加依赖关系的,响应数据变化。 Provider继承了InheritedProvider。 提供了.of方法,与我们所写的Provider大致是相同的。

value & _startListening

先看下继承和关联关系

classDiagram
InheritedProvider <|-- Provider
Provider .. Consumer
ListenableProvider <|-- ChangeNotifierProvider
InheritedProvider <|-- ListenableProvider
class InheritedProvider{
+_ValueInheritedProvider _delegate
}
InheritedProvider .. _ValueInheritedProvider
_ValueInheritedProvider .. _ValueInheritedProviderState

ChangeNotifierProvider.value构造方法往上追溯可以得知: ListenableProvider具有静态函数_startListening

class ListenableProvider{
    static VoidCallback _startListening(InheritedContext e,Listenable? value) {
    value?.addListener(e.markNeedsNotifyDependents);
    return () => value?.removeListener(e.markNeedsNotifyDependents);
  }
}

最终我们的 value T_startListening都给了_ValueInheritedProvider

class _ValueInheritedProvider<T> extends _Delegate<T> {
 
  final T value;
  ///上面的流程没有传递这个值
  final UpdateShouldNotify<T>? _updateShouldNotify;
  final StartListening<T>? startListening;

  @override
  _ValueInheritedProviderState<T> createState() {
    return _ValueInheritedProviderState<T>();
  }
}

class _ValueInheritedProviderState<T>
    extends _DelegateState<T, _ValueInheritedProvider<T>> {
  VoidCallback? _removeListener;

   ///此方法什么时候调用?
  @override
  T get value {
    _removeListener ??= delegate.startListening?.call(element!, delegate.value);
    return delegate.value;
  }

  @override
  bool willUpdateDelegate(_ValueInheritedProvider<T> newDelegate) {
    bool shouldNotify = newDelegate.value != delegate.value;
    if (shouldNotify && _removeListener != null) {
      _removeListener!();
      _removeListener = null;
    }
    return shouldNotify;
  }
  ...

_startListeningT get value中调用。 那T get value什么时候调用? 首次调用 Provider.of()时。

class Consumer<T> extends SingleChildStatelessWidget {
  @override
  Widget buildWithChild(BuildContext context, Widget? child) {
    return builder(
      context,
      Provider.of<T>(context),
      child,
    );
  }
}
class Provider extends InheritedProvider<T>{
     static T of<T>(BuildContext context, {bool listen = true}) {
        ....
        return inheritedElement.value;
     }
}

class _InheritedProviderScopeElement{
    @override
    T get value => _delegateState.value;
}

总之,addListener首次是在Element mount过程中调用的。

ChangeNotifier.notifyListeners过程

notifyListeners()最终调用到_InheritedProviderScopeElement.markNeedsNotifyDependents

class CountModel extends ChangeNotifier {
  int count;
  CountModel(this.count);
  void increment() {
    count++;
    notifyListeners();
  }
}

我们进入到 ChangeNotifier.notifyListeners(),遍历并且执行注册的listener。

class ChangeNotifier{

  void notifyListeners() {
      final List<VoidCallback> localListeners = List<VoidCallback>.from(_listeners);
      for (VoidCallback listener in localListeners) {
          if (_listeners.contains(listener)) listener();
      }
    }
  }
}

进入下面

class _InheritedProviderScopeElement{
  @override
  void markNeedsNotifyDependents() {
    if (!_isNotifyDependentsEnabled) return;
    markNeedsBuild();
    _shouldNotifyDependents = true;
  }
}

接下来就是_InheritedProviderScopeElement.performRebuild()

回顾InheritedElement,是调用updateChild方法-->updated-->notifyClients-->notifyDependent。

重载了build()方法,notifyClients()放到到super.build()前。notifyClients的逻辑就是InheritedElement定义的。dependent的刷新ok了。

class _InheritedProviderScopeElement

@override
  Widget build() {
    ...
    _delegateState.build(_isBuildFromExternalSources);
    if (_shouldNotifyDependents) {
      _shouldNotifyDependents = false;
      notifyClients(widget);
    }
    return super.build();
  }

InheritedElement的特性就在于不会层层update它底下的element,而只是调用notifyClients,update有依赖的element。

从代码的分析来看,我们的视角始终是围绕着Element, Widget用来构建/更新Element。

StreamProvider

InheritedProvider的众多子类分成了以下分支:

  • Provider:需要手动管理监听
  • ListenableProvider:不需要手动管理监听,但泛型参数T要继承Listenable。
    • ChangeNotifierProvider
  • DeferredInheritedProvider
    • ValueListenableProvider
    • FutureProvider
    • StreamProvider DeferredInheritedProvider,相对ListenableProviderT本身具有监听功能。Deferred*不直接监听T,但T是如下形式:Stream,Future,ValueListenable。

Flutter基于Stream,做了一套状态管理的StreamBuilder。 ChangeNotifierProvider已经可以满足现在的使用场景,暂时还没用到StreamProvider。