美文网首页Flutter
Flutter 学习之旅(三十八) Flutter Elemen

Flutter 学习之旅(三十八) Flutter Elemen

作者: Tsm_2020 | 来源:发表于2020-09-27 14:51 被阅读0次

    Flutter 中 Element 作用的是作为中枢来管理和调度Widget和RenderObject,
    这里我们主要说一下RenderObjectWidget 来主要说一下Element 的生命周期,这里我删除了一些assert 的方法,方便查看

    RenderObjectElement

    1.RenderObjectElement 的 mount 方法

      @override
      void mount(Element parent, dynamic newSlot) {
        super.mount(parent, newSlot);
        _renderObject = widget.createRenderObject(this);
        attachRenderObject(newSlot);
        _dirty = false;
      }
    

    Element 的 mount 方法

      void mount(Element parent, dynamic newSlot) {
        _parent = parent;
        _slot = newSlot;
        _depth = _parent != null ? _parent.depth + 1 : 1;
        _active = true;
        if (parent != null)
          _owner = parent.owner;
        final Key key = widget.key;
        if (key is GlobalKey) {
          key._register(this);
        }
        _updateInheritance();
      }
    

    RenderObjectElement 中的 mount 方法

    1.先调用super 的mount,初始化了parent和slot (卡槽),同时将active 变为true , _debugLifecycleState 的状态变更为active,表示控件已经初始化了,并且可以被加载了,.在这里同时共享了parent 的inheritedWidget,这里也就解释了为什么子控件能获取到父控件inheritedWidget,子控件通过持有element 间接持有了inheritedWidget
    2.createRenderObject 并 attachRenderObject

    UpdateChild

    在flutter widget树中的结构或者数据发生变化的时候,为了避免大量重复创建element,他会先判断是否可以更新element,他会调用Element 的updateChild 方法,这里 Widget.canUpdate 判断是否可以更新,主要判断新旧widget 的runtimeType和key是否相同,这里如果想要强制刷新控件,只需要给定不同的key就可以了

      @protected
      Element updateChild(Element child, Widget newWidget, dynamic newSlot) {
        if (newWidget == null) {
          if (child != null)
            deactivateChild(child);///没有新控件  并且有原来的控件,则移出原来的控件,
          return null;
        }
        Element newChild;
        if (child != null) {///原来有child
          if (hasSameSuperclass && child.widget == newWidget) {///相同的父控件类型,相同的子控件,直接更新child
            if (child.slot != newSlot)
              updateSlotForChild(child, newSlot);
            newChild = child;
          } else if (hasSameSuperclass && Widget.canUpdate(child.widget, newWidget)) {
            ///相同的父控件类型,并且可以更新widget,则更新child
            if (child.slot != newSlot)
              updateSlotForChild(child, newSlot);
            child.update(newWidget);
            assert(child.widget == newWidget);
            newChild = child;
          } else {
            ////不能更新,则需要先移出原有child,再创建新的child并添加
            deactivateChild(child);
            assert(child._parent == null);
            newChild = inflateWidget(newWidget, newSlot);
          }
        } else {
          ///原来没有child ,直接创建新的child,并添加
          newChild = inflateWidget(newWidget, newSlot);
        }
        return newChild;
      }
    

    除了更新widget,其他的方法都比较简单,没有子方法,这里说一下如果父控件类型相同,并且可以更新的是否,调用update方法,

      ///更新RenderObject
      @override
      void update(covariant RenderObjectWidget newWidget) {
        super.update(newWidget);
        widget.updateRenderObject(this, renderObject);
        _dirty = false;
      }
    
    ///更新element
      @mustCallSuper
      void update(covariant Widget newWidget) {
        assert(_debugLifecycleState == _ElementLifecycle.active
            && widget != null
            && newWidget != null
            && newWidget != widget
            && depth != null
            && _active
            && Widget.canUpdate(widget, newWidget));
        _widget = newWidget;
      }
    

    这里update 方法,先调用父类的update方法,更新了widget,同时调用了widget 的updateRenderObject

    deactivateChild

    上面updatechild 方法里面,如果新的widget为空,并且存在旧的child,就会调用deactivateChild移出child,

      @protected
      void deactivateChild(Element child) {
        child._parent = null;
        child.detachRenderObject();
        owner._inactiveElements.add(child); 
      }
    

    该方法只是将element 移动到 inactive 列表中,并且调用child的 detachRenderObject,

    framwork 后续会调用 deactivate 方法将_debugLifecycleState 状态置为inactive ,

    ///移出element
      @mustCallSuper
      void deactivate() {
        if (_dependencies != null && _dependencies.isNotEmpty) {
          for (final InheritedElement dependency in _dependencies)
            dependency._dependents.remove(this);
        }
        _inheritedWidgets = null;
        _active = false;
      }
    

    这里重置inheritedwidget,和active , 此时控件将不会显示在屏幕上,后续framwork 会调用unmount 方法,移出该element

    移出后重新加入element树

    在updatechild 方法中,如果需要重新创建widget,首先需要填充widget,在此过程中,如果key是GlobalKey,
    那么Framework会先将element从现有位置移除,然后再调用其activate方法,并将其renderObject重新attach到渲染树

      ////重新创建widget
      @protected
      Element inflateWidget(Widget newWidget, dynamic newSlot) {
        final Key key = newWidget.key;
        if (key is GlobalKey) {///如果是GlobalKey  则复用 element
          ///利用Globalkey 查找element
          final Element newChild = _retakeInactiveElement(key, newWidget);
          if (newChild != null) {
            assert(newChild._parent == null);
            newChild._activateWithParent(this, newSlot);///重新将element 的状态变成active,并attachRenderObject
            
            ///找到element 后 重新updateChild ,肯定是走可以更新widget方法,这个根据_retakeInactiveElement推断出来的,要不然无限      
            ///循环inflateWidget
            final Element updatedChild = updateChild(newChild, newWidget, newSlot);
            assert(newChild == updatedChild);
            return updatedChild;
          }
        }
    
        ///不是GlobalKey  或者根据GlobalKey找不到element ,重新创建,并初始化 mount
        final Element newChild = newWidget.createElement();
        newChild.mount(this, newSlot);
        assert(newChild._debugLifecycleState == _ElementLifecycle.active);
        return newChild;
      }
      
    
    
      ///根据GlobalKey   查找element
      Element _retakeInactiveElement(GlobalKey key, Widget newWidget) {
        final Element element = key._currentElement;///找不到
        if (element == null)
          return null;
        //不能更新则打断,这里判断,如果找到element对应的widget可以更新,则重新updatechild后走可以更新分支,如果走重新创建则无  
        //线循环了
        if (!Widget.canUpdate(element.widget, newWidget))
          return null;
    
        final Element parent = element._parent;
        if (parent != null) {
          parent.forgetChild(element);
          parent.deactivateChild(element);///parent 放弃该element ,
        }
        assert(element._parent == null);
        owner._inactiveElements.remove(element);///将这个控件从inactiveElements 中移出,让他可以重新被渲染,
        return element;
      }
    
    
    
      ///重现将element 的状态变成active,并attachRenderObject
      void _activateWithParent(Element parent, dynamic newSlot) {
        assert(_debugLifecycleState == _ElementLifecycle.inactive);
        _parent = parent;
        _updateDepth(_parent.depth);
        _activateRecursively(this);
        attachRenderObject(newSlot);
      }
    
      static void _activateRecursively(Element element) {
        element.activate();
        element.visitChildren(_activateRecursively);
      }
    

    至此整个element的生命周期就结束了,我查看的源码是Flutter 1.20.3,由于flutter 的代码更新的比较快,所以大家看的版本可能和我不同,但是原理应该都差不多,整个学习的思路就是这些,从网上看了很多资料,但是绝大部分都是copy一个地方的,对于初学者来说还是有一些难度的,我在这里总结的都是最基础,仔细看一下难度应该不大,希望对大家学习有一些帮助,

    在这里进行一下element 整个生命周期做最后总结

    1.framwork 通过调用mount 方法,改变element 中 activite状态和初始化inheritedWidget,并且创建RenderObject,并attachRenderObject,
    2.updatechild 当widget树结构发生变化,framwork 会调用 updatechild
    这个流程也就是在这里分流
    这里分了几种情况
    1>.不存在原始child,则新创建新的widget,并重置element,关联renderobject
    2>.如果新旧控件的类型相同,并且控件也相同,直接更新wiget
    3>.如果新旧控件的类型相同,并且这个wiget 能复用(即widget.canupdate为true),则更新widget,并执行updateRenderObject
    4>.如果新旧控件类型不同,则移出原有widget,并重新创建新的widget,并重置element,关联renderobject
    3.update过程中涉及到新建widget,
    1>.这个过程如果使用的是GlobalKey,则查找element ,找到后重新执行updatechild方法,更新widget,并将它从原来的节点移出,并重新active,关联RenderObject,
    2>.如果不是GlobalKey,或者根据GlobalKey查找的widget 不能更新,则创建element 并执行mount 方法
    4.移除element,首先先调用deactivateChild ,取消关联RenderObject ,并将element 添加到inactiveElements
    中,后续framwork会调用deactivate方法,将element 从element树中移除,此时该widget 不能在widget树中显示

    至此整个流程就结束了

    Widget 与 Element 关系

    接下来再说一下从网上看到的Widget 与 Element 这种多对一的映射关系,其实我在刚开始看的时候觉得这种多对一的关系是在创建不同Widget 的时候,统一交给同一个Element 去管理他们的关系,是以这种方式来表现的多对一,看了Element 源码后,我觉得他们关系如果在生命周期的角度来看其实是一对一的映射关系,为什么这么说,先来说一下Flutter的机制,由于Flutter 所有的变量都是final 类型的,首次创建该Widget的时候,会创建一个Elemnt ,当这个View 有更新的时候,其实是又生成了一个Widget ,只不过新生成的这个Widget 替代了原有的Widgget 与Element 之间建立的关系,所以就形成了Element 与 Widget形成了这种一对多的关系,这个多并不是所有的Widget,而是同一个Widget 的在被创建了多次后产生的Widget

      void update(covariant Widget newWidget) {
        // This code is hot when hot reloading, so we try to
        // only call _AssertionError._evaluateAssertion once.
        assert(_debugLifecycleState == _ElementLifecycle.active
            && widget != null
            && newWidget != null
            && newWidget != widget
            && depth != null
            && _active
            && Widget.canUpdate(widget, newWidget));
        // This Element was told to update and we can now release all the global key
        // reservations of forgotten children. We cannot do this earlier because the
        // forgotten children still represent global key duplications if the element
        // never updates (the forgotten children are not removed from the tree
        // until the call to update happens)
        assert(() {
          _debugForgottenChildrenWithGlobalKey.forEach(_debugRemoveGlobalKeyReservation);
          _debugForgottenChildrenWithGlobalKey.clear();
          return true;
        }());
        _widget = newWidget;
      }
    

    这种灵感就来源于Element 的update 的方法,

    如果大家有不同的见解,欢迎指教

    我学习flutter的整个过程都记录在里面了
    https://www.jianshu.com/c/36554cb4c804

    最后附上demo 地址

    https://github.com/tsm19911014/tsm_flutter

    相关文章

      网友评论

        本文标题:Flutter 学习之旅(三十八) Flutter Elemen

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