Flutter

InheritedWidget内部实现原理浅析

2020-06-11  本文已影响0人  HawkFlying

使用示例

阅读原理之前请先体验InheritedWidget的使用demo:数据传递/状态管理 一InheritedWidget使用示例

实现原理分析

InheritedWidget定义

先看一下InheritedWidget的定义:

abstract class InheritedWidget extends ProxyWidget {
  /// Abstract const constructor. This constructor enables subclasses to provide
  /// const constructors so that they can be used in const expressions.
  const InheritedWidget({ Key key, Widget child })
    : super(key: key, child: child);

  @override
  InheritedElement createElement() => InheritedElement(this);

  /// Whether the framework should notify widgets that inherit from this widget.
  ///
  /// When this widget is rebuilt, sometimes we need to rebuild the widgets that
  /// inherit from this widget but sometimes we do not. For example, if the data
  /// held by this widget is the same as the data held by `oldWidget`, then we
  /// do not need to rebuild the widgets that inherited the data held by
  /// `oldWidget`.
  ///
  /// The framework distinguishes these cases by calling this function with the
  /// widget that previously occupied this location in the tree as an argument.
  /// The given widget is guaranteed to have the same [runtimeType] as this
  /// object.
  @protected
  bool updateShouldNotify(covariant InheritedWidget oldWidget);
}

InheritedWidget是一个继承自 ProxyWidget 的抽象类。除了实现了一个 createElement 方法之外,还定义了一个 updateShouldNotify() 接口。 updateShouldNotify就是使用InheritedWidget要实现的接口,决定InheritedWidget变化时,要不要通知子widget。

再看下ProxyWidget是什么鬼:

abstract class ProxyWidget extends Widget {
  /// Creates a widget that has exactly one child widget.
  const ProxyWidget({ Key key, @required this.child }) : super(key: key);

  /// The widget below this widget in the tree.
  ///
  /// {@template flutter.widgets.child}
  /// This widget can only have one child. To lay out multiple children, let this
  /// widget's child be a widget such as [Row], [Column], or [Stack], which have a
  /// `children` property, and then provide the children to that widget.
  /// {@endtemplate}
  final Widget child;
}

ProxyWidget里面没什么特别的东西

InheritedWidget 共享数据传递机制

既然在InheritedWidget定义上没有收获,我们就从InheritedWidget使用上入手,每个自定义InheritedWidget都会实现一个 of 静态方法,这个of静态方法是提供给InheritedWidget的子widget来访问自定义InheritedWidget的,先看下我们示例中of方法怎么实现:

  static ShareDataInheritedWidget of(BuildContext context) {
    return context.dependOnInheritedWidgetOfExactType();
  }

of方法直接返回context的dependOnInheritedWidgetOfExactType,看下dependOnInheritedWidgetOfExactType里面是什么:
dependOnInheritedWidgetOfExactType在源码中两处,一处是BuildContext,一处是Element:


image.png

BuildContext中dependOnInheritedWidgetOfExactType其实只是定义:

 T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({ Object aspect });

真正实现dependOnInheritedWidgetOfExactType是在Element (其实BuildContext就是Element的引用,这里就不作分析了) :

@override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return dependOnInheritedElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

 @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

可以看到,子widget通过_inheritedWidgets和我们自定义的InheritedWidget拿到对应的InheritedElement,再从InheritedElement中拿到我们自定义的InheritedWidget;

_inheritedWidgets定义如下:

Map<Type, InheritedElement> _inheritedWidgets;

_inheritedWidgets是一个Map类型,以Type为key,定义在Element中,也就是每个Elemnet都有一个_inheritedWidgets;

这里插一段小曲:
Type定义如下:

/**
 * Runtime representation of a type.
 */
abstract class Type {}

可以在Object中找到它:

class Object {
...
  /**
   * A representation of the runtime type of the object.
   */
  external Type get runtimeType;
...
}

Object本身应该就实现Type,类继承自Object,所以类也是Object,可以直接传给上面Map类型为Type的 key,也就是传给Map Type key其实传的是类的runtimeType,经验证,多次以相同类名作为key保存value到Map,最后以这个类名为key取出的value为最后存储的value,类名作为key跟定义一个字符串作为key作用类似;

回到正题,我们看下_inheritedWidgets数据怎么来,因为_inheritedWidgets在Element中定义,所以在Element中先找:

void _updateInheritance() {
    assert(_active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

发现_inheritedWidgets在_updateInheritance方法里初始化,_updateInheritance是在widget mounted或activate调用;知道_inheritedWidgets的数据来自父widget,那父widget的_inheritedWidgets从哪都是在来呢?
在InheritedWidget的定义中,发现InheritedWidget有自己的Element->InheritedElement

abstract class InheritedWidget extends ProxyWidget {
...
  @override
  InheritedElement createElement() => InheritedElement(this);
...

再看下InheritedElement源码:

/// An [Element] that uses an [InheritedWidget] as its configuration.
class InheritedElement extends ProxyElement {
  /// Creates an element that uses the given widget as its configuration.
  InheritedElement(InheritedWidget widget) : super(widget);

  @override
  InheritedWidget get widget => super.widget;

  final Map<Element, Object> _dependents = HashMap<Element, Object>();

  @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;
  }

可以看到InheritedElement重写了_updateInheritance方法,当父widget的_inheritedWidgets为空时,实例化_inheritedWidgets;当父widget不为空时,也实例化_inheritedWidgets,并把父widget的_inheritedWidgets加进来,这里有个细节,为什么不直接用父widget的_inheritedWidgets呢,这个后面会分析;最后关键的一步,就是将当前自定义InheritedWidget对应的Element保存到以自定义InheritedWidget类别作为key的_inheritedWidgets里,这样_inheritedWidgets就保存了我们自定义InheritedWidget的数据了;InheritedWidget的_inheritedWidgets传给子widget,子widget就可以通过_inheritedWidgets拿到我们自定义InheritedWidget的数据了;

到这里我们可以知道:(1)、每个widget对应的Element都有一个_inheritedWidgets,widget树自上而下,一级一级将自己的_inheritedWidgets传给下一级,并且当前级别为InheritedWidget时,就会将当前自定义的InheritedWidget保存在_inheritedWidgets;
(2)、通过_inheritedWidgets一级一级的向下传递,这样每个子widget的_inheritedWidgets就拥有上级父widget的所有类别的InheritedWidget,就可以通过自身的_inheritedWidgets访问到对应的InheritedWidget;

InheritedWidget 数据更新通知机制

我们自定义的变化InheritedWidget是怎么知道到子widget的didChangeDependencies呢?
子widget跟父InheritedWidget使用关联只有of静态方法,所以我们再从of方法里仔细分析:

 @override
  InheritedWidget dependOnInheritedElement(InheritedElement ancestor, { Object aspect }) {
    assert(ancestor != null);
    _dependencies ??= HashSet<InheritedElement>();
    _dependencies.add(ancestor);
    ancestor.updateDependencies(this, aspect);
    return ancestor.widget;
  }

发现在子widget的dependOnInheritedElement方法里,InheritedWidget的element->InheritedElement,也就是ancestor,调用了updateDependencies方法将子widget的element传递过去;
来看下InheritedElement的updateDependencies的方法:

class InheritedElement extends ProxyElement {
...
final Map<Element, Object> _dependents = HashMap<Element, Object>();
@protected
  void updateDependencies(Element dependent, Object aspect) {
    setDependencies(dependent, null);
  }
  @protected
  void setDependencies(Element dependent, Object value) {
    _dependents[dependent] = value;
  }

InheritedElement的updateDependencies方法将子widget对应的element作为key,保存在InheritedElement的_dependents中,这样我们自定义的InheritedWidget就可以通过自身的InheritedElement,找到关联的子widget的element了;
我们定义的的InheritedWidget有了需要通知的子widget,再看看InheritedWidget如何在变化时通知子widget的;
在自定义InheritedWidget时,我们需要实现接口方法:updateShouldNotify,先找下updateShouldNotify在哪里调用到:


来自ProxyElement:
  @override
  void update(ProxyWidget newWidget) {
    ...
    updated(oldWidget);
    ...
  }

来自InheritedElement:
  @override
  void updated(InheritedWidget oldWidget) {
    if (widget.updateShouldNotify(oldWidget))
      super.updated(oldWidget);
  }

来自ProxyElement:
  @protected
  void updated(covariant ProxyWidget oldWidget) {
    notifyClients(oldWidget);
  } 

来自InheritedElement:
  @override
  void notifyClients(InheritedWidget oldWidget) { 
    for (Element dependent in _dependents.keys) {
      notifyDependent(oldWidget, dependent);
    }
  }
}

来自InheritedElement:
  @protected
  void notifyDependent(covariant InheritedWidget oldWidget, Element dependent) {
    dependent.didChangeDependencies();
  }

这里比较绕,需要仔细理解InheritedElement 和 ProxyElement关系(InheritedElement extends ProxyElement),update和updated方法区别;
由此可知:

自定义of静态方法内部实现用getElementForInheritedWidgetOfExactType为何子widget的didChangeDependencies就不会接收到InheritedWidget更新通知?

我们知道,自定义of静态方法内部实现用dependOnInheritedWidgetOfExactType子widget的didChangeDependencies就会接收到InheritedWidget更新,所以先看下两个方法内部实现的差异:
dependOnInheritedWidgetOfExactType:

  @override
  T dependOnInheritedWidgetOfExactType<T extends InheritedWidget>({Object aspect}) {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    if (ancestor != null) {
      assert(ancestor is InheritedElement);
      return dependOnInheritedElement(ancestor, aspect: aspect);
    }
    _hadUnsatisfiedDependencies = true;
    return null;
  }

getElementForInheritedWidgetOfExactType:

  @override
  InheritedElement getElementForInheritedWidgetOfExactType<T extends InheritedWidget>() {
    assert(_debugCheckStateIsActiveForAncestorLookup());
    final InheritedElement ancestor = _inheritedWidgets == null ? null : _inheritedWidgets[T];
    return ancestor;
  }

从两个方法内部代码实现对比可知:

Widget树上有多个相同类别的InheritedWidget,为何子widget只会找到最近的父InheritedWidget?

从上面分析我们知道,每个子widget的_inheritedWidgets数据初始化是在_updateInheritance 方法进行的,其实InheritedElement重写了_updateInheritance方法,如下:,


image.png

普通widget的Element的_updateInheritance方法:

 void _updateInheritance() {
    assert(_active);
    _inheritedWidgets = _parent?._inheritedWidgets;
  }

InheritedWidget的InheritedElement的_updateInheritance方法:

  @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;
  }

对比上面代码,我们可以看到,普通widget只是把父widget的_inheritedWidgets直接赋值到自身的_inheritedWidgets;而InheritedWidget是先创建新的_inheritedWidgets,再把父widget的_inheritedWidgets的数据加到自己创建的_inheritedWidgets中,同时以自己的类别名作为key,将自己的element实例保存到自己创建的_inheritedWidgets;
为什么InheritedWidget要自己创建_inheritedWidgets呢?
这样做的好处是:
(1)、因为如果直接用父的_inheritedWidgets,当将自身的element加到_inheritedWidgets时,如果父的_inheritedWidgets数据中有和自身相同类别的InheritedWidget数据,就会把父_inheritedWidgets和自身相同类别的InheritedWidget的数据替换掉,也就是一级传一传的_inheritedWidgets,最终只有一种类别的InheritedWidget,这样传递数据就会混乱;
(2)、这也是为什么子widget只会找到最近的父InheritedWidget的原因:因为在这层的InheritedWidget创建新的_inheritedWidgets,并以自身的类别作为key,将自己的element实例保存到自己创建的_inheritedWidgets,如果_inheritedWidgets中有和自身相同类别的InheritedWidget数据,替换的也只是自身创建的_inheritedWidgets中数据,不会替换掉父_inheritedWidgets中和自己相同类别保存的数据;然后再将自身的_inheritedWidgets传给子widget,这样子widget通过of静态方法拿到的InheritedWidget就是离它最近的父InheritedWidget的数据了。

上一篇 下一篇

猜你喜欢

热点阅读