美文网首页
flutter_run_app_analyze

flutter_run_app_analyze

作者: Wi1ls努力努力再努力 | 来源:发表于2020-09-25 11:04 被阅读0次

    Flutter源码版本为1.12.13

    用做简单的例子来讲,这个用户布局只有一个TextView

    void main() => runApp(
        Text("你好",
        textDirection: TextDirection.ltr),
    );
    

    首先来看runApp()方法:

    void run(Widgetr app){
        WidgetsFultterBinding.ensureInitialized()
            ..scheduleAttachRootWidget(app)
            ..scheduleWarmUpFrame();
    }
    

    • Step1.前置环境,主要是在WidgetsFultterBinding.ensureInitialized()

    首先来看WidgetsFultterBinding的定义,这个类从右WidgetBinding往左GestureBinding进行初始化,下面只罗列关键的一些初始化

    class WidgetFlutterBinding extends BindingBase with GestureBinding, ServiceBinding, ScheduleBinding, PaintingBinding, SemanticsBinding, RenderBinding, WidgetBinding{
        ...
    }
    
    
    @WidgetBinding
    mixin WidgetBinding{
        BuildOwner _buildOwner;
    
        void initInstances(){
            _buildOwner = BuildOwner();
        }
    }
    
    @RenderBinding
    mixin RenderBinding{
        PipelineOwner _pipelineOwner;
        void initInstance(){
            _pipelineOwner = PipelineOwner(...);
            initRenderView();
            addPersistentFrameCallback(_handlePersistentFrameCallback);
        }
        
        void initRenerView(){
            render = Render(...);
            render.prepareInitialFrame();
        }
        
        set renderView(RenderView value){
            _pipelineOwner.rootNode = value;
        }
    }
    
    @PipelineOwner
    class PipelineOnwer{
        AbstractNode _rootNode;
        
        set rootNode(AbstractNode value){
            _rootNode?.attach(this);
        }
    }
    
    @RendView
    void prepareInitialFrame(){
        scheduleInitialLayout();
        sceduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
    }
    
    void addPersistentFrameCallback(FrameCallback callback){
        _persistentCallbacks.add(callback);
    }
    
    void _handlePersistentFrameCallback(Duration timeStamp){
        drawFrame();
    }
    
    
    

    至此,需要的前置工作都完成了,生成了一个PipelineOwner与RenderView并且互相持有。创建了一个BuildOnwer,在_persistentCallbacks中注册了回调函数_handlePersistentFrameCallback(),最终调用到drawFrame();


    • Step2 . scheduleAttachRootWidget(Widget)@WidgetBinding
    void scheduleAttachRootWidget(Widget rootWidget){
        Timer.run((){
            attachRootWidget(rootWidget);
        });
    }
    
    Element get renderViewElement => _renerViewElement;
    Element _renderViewElement;
    
    void attachRootWidget(Widget rootWidget){
        //这里先生成一个RenderObjectToWidgetAdapter<Render>对象,再调用attachToRenerTree()生成一个Element,该Element为根
        _renderViewElement = RenderObjectToWidgetAdapter<Render>(
            container: renderView,
            chhild:rootWidget,
        ).attachToRenderTree(buildOwner, renderViewElement);
    }
    
    @RenderObjectToWidgetAdapter<Render>
    class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget{
        //就是用户自定义的Widget
        final Widget child;
        //就是RenderView对象
        final RenderObjectWithChildMixin<T> container;
        
        RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
        
        RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
        
        //重点
        RenderObjectToWidgetElement<T> attachToRenderTree(BuildOwner owner,[RenderObjectToWidgetElement<T> element]){
            if(element == null){
                //命中,入参element此时为null
                //lockState()@BuildOwner会调用callback
                owner.lockState((){
                    //可以看到,element会RenderObjectToWidgetElement<RenderBox>对象,并且内部的widget为this,即RenderObjectToWidgetAdapter<RenderBox>
                    element = createElement();
                    element.assignOwner(owner);
                });
                owner.buildScope(element, (){
                    //调用mount()函数入参均为0,这个方法在第三步讨论
                    element.mount(null,null);
                });
            }else{
                ...
            }
            return element
        }
    }
    
    
    • Step3. mount(null,null)@RenderObjectToWidgetElement<RenderBox>
    @RenderObjectToWidgetElement
    void mount(Element parent, dynamic newSlot){
        super.mount(parent,new Slot);
        _rebuild();
    }
    
    @RenderObjectElement
    class RenderObjectElement{
        RenderObject _renderObject;
        void mount(Element parent, dynamic newSlot){
            //对于REnderObjectToWidgetElement,widget就是RenderObjectToWidgetAdapter
            //于是返回的_renderObject就是RenderView
            _renderObject = widget.createRenderObject(this);
            attachRenderObject(newSlot);
        }
        void attachRenderObject(dynamic newSlot){
            //重点,是在这里进行生成RenderObject树的
            //_findAncestoreRenderObjectElement是向上寻找最近的RenderObjectElement,只有该Element才有RenderObject,
            //找到后将其作为当前RenderObject的parent,属于递归
            //在这里是根Element,所以不会执行
            _ancestoreRenderObjectElement = _findAncestoreRenderObjectElement();
            //由子类实现,一般来说是把自身挂到最近的父RenderObject上,递归生成RenderObject树
            _ancestoreREnderObjectElement?.insertChildREnderObject(renderObject, newSlot);
        }
        
        RenderObjectElement _findAncestoreRenderObjectElement(){
            Element ancestore = _parent;
            while(ancestore!=null && ancestore is! RenderObjectElement){
                ancestore = ancestore._parent;
            }
            return ancestore;
        }
    }
    
    //也是很关键,在这里方法里会进行递归,构造Widget树,Element树,RenderObject树
    void _rebuild(){
        //在这里,_child为null,widget为RenderObjectToWidgetAdapter<BoxRender>,
        //于是widget.chil即为我们自定义的Widget,在这里就是Text()
        _child = updateChild(_child, widget.child, _rootChildSlot);
    }
    
    Element updateChild(Element child, Widget newWidget, dynamic newSlot){
        //因为child为null,于是直接是最后一步
        return inflateWidget(newWidget, newSlot);
    }
    
    Element inflateWidget(Widget widget, dynamic newSlot){
        //创建子,即Text的Element
        //由具体的子类实现
        final Element newChild=newWidget.createElement();
        //该方法会递归构造Widget树,Element树,RenderObject树
        //该方法由具体的子类实现
        newChild.mount(this, newSlot);
        //返回子的Element,并且挂载在当前RenderObjectToWidgetElement的_child上
        return newChild;
    }
    
    

    于是,我们拥有了一个RenderView和Pipeline对象,互相持有。
    一个RenderObjectToWidgetElement<BoxRender>,其_child为Text(自己实现的Widget).createElement()对象,其widget成员变量为RenderObjectToWidgetAdapter<BoxREnder>,其_renderObject变量为RenderView
    一个RenderObjectToWidgetAdapter<BoxRender对象>,其child为用户自定义的Widget,container为RenderView。该对象如其名,Adapter,属于一个桥接的功能。


    • Step4 三颗树的构造。

    我们的定义的子类的Text,因此要去Text的源码里面看了,之前分析到newWidget.createElement()/newChild.mount(this,newSlot).【newWidget为Text,this指向RenderObjectToWidgetElement,后面可以知道newChild为newWidget调用crateElement()创建的StatelessElement对象】

    首先来看Text类的继承结构

    Text -> StatelessWidget -> Widget
    //Text没有重写createElement(),该方法在StatelessWidget实现
    
    @StatelessWidget
    abstract class StatelessWidget extends Widget{
        //执行完之后,Text创建了一个StatelessElement,其内部的widget即为Text本身,
        //并且将该StatelessElement作为RenderObjectToWidgetElement<BoxRender>的_child进行挂载
        StatelessElement createElement() => StatelessElement(this);
    }
    
    //StatelessElement继承自ComponentElement
    
    @ComponentElement
    abstract class ComponentElement extends Element{
        //在这里parent为根RenderObjectToWidgetElement
        void mount(Element parent, dynamic newSlot){
            //super.mount()将RenderObjectToWidgetElement最为parent进行挂载
            //将其owner作为本身的owner即BuildOwner,同时更新树的深度
            super.mount(parent, newSlot);
            _firstBuild();
        }
        
        void _firstBuild(){
            rebuild();
        }
        
        @Element
        void rebuild(){
            ...
            performRebuild();
        }
        
        @ComponeneElement
        void performRebuild(){
            Widget build=build();
            //这里开始递归调用了,同时_child为null,会执行inflateWidget(),
            //已经分析过了,在递归挂载
            //从下面分析可以知道,这里的build是RichText对象
            _child=updateChild(_child, build, slot);
        }
    }
    
    @StatelessElement
    class StatelessElemet extends ComponentElement{
        //widgt为Text
        Widget() => widget.build(this);
    }
    
    @Text
    class Text{
        Widget build(BuildCOntext context){
            Widget result=RichText(...);
            return result;
        }
    }
    
    //于是开始调用RichText的createElement()与 update@Element();
    class RichText extends MultiChildRenderObjectWidget{
        _MouseREgionElement createElement()=>_MouseRegionElement(this);
    }
    
    class _MouseRegionElement extends SigleChildRenderObjectElement(){
        
    }
    
    class SingleChildREnderObjectElement extends RenderObjectElement{
        void mount(Element parent, dynamic newslot){
            super.mount(parent, newSLot)
            _child = updateChild(_child, widget.child, null);
        }
    }
    
    class RenderObjectElement{
        void mount(Element element, dynamic, newSlot){
            super.mount(parent, newSlot);
            _renderObject = widget.createRenderObject(this);
            //在这里挂载RenderObject树
            attachREnderObject(newSlot);
        }
        
        void attachRenderObject(dynamic newSlot){
            //向上寻找最近的RenderObject,在这里就是REnderObjectToWidgetElement<BoxRender>
            _ancestorRenderObjectElement=_findAncestoreRenderObjectElement();
            //父类实现
            _ancestorRenderObjectElement?.insertChildRenderObject(renderObject, newSLot);
        }
        
        @RenderBojectToWidgetElement
        void insertChildRenderObject(RenderObject child, dynamic slot){
            //将RichText创建的RenderObject挂载,最为child
            renderObject.child = child;
        }
        
        //来看看挂载的RenderObject是什么
        @TextRich
        RenderParagaph createRenderObject(BuildContext context){
            return RenderParagraph();
        }
    }
    

    可以看到,在向下递归的过程中并且会将子节点的Element挂载到当前节点的Element生成Element树,如果当前节点Element为RenderObjectElement的子类,则在mount()的过程中,会将该节点的RenderObject挂载到父RenderObjectElement上,生成RenderObject树。

    要注意的是Widget其实不会生成一颗树,在Widget的属性类别也没有【Widget child】这个属性,在代码中也找不到树的生成,从父Widget也无法直接访问到子Widget,从子Widget也无法访问到父Widget。那么我们说的Widget树到底是什么东西。

    在Flutter中,一般树特指Element树,因为每个Element都持有其对应的Widget,并且可以根据Element树生成对应的RenderObject树。


    • Step5

    到此,我们在RenderObjectToWidgetElement中就构造好了Element树,RenderObject树,分别对应_child@RenderObjectToWidgetElement, _renderObject@RenderObjectToWidgetElement。

    下面就开始进行渲染的过程了


    • Setp6 渲染

    下面要分析的是scheduleWarmUpFrame()函数【runApp()】

    @ScheduleBinding
    void scheduleWarmUpFrame(){
        Timer.run((){
            //不用关注
            handleBeginFrame(null);
        });
        Timer.run((){
            //重点
            handleDrawFrame();
        });
        //这里就是调用engine进行绘制
        //scheduleFrame()@window   属于engine的native方法
        scheduleFrame();
    }
    
    
    void handleDrawFrame(){
        //设定目前正在进行persistentCallbacks状态
        _schedulerPhase = SchedulePhase.phersistentCallbacks;
        //最上面分析过,最重要的一个callback在RenderBinding构造的时候添加了
        //即doFrame()@RenderBinding
        for(FrameCallback callback in _persistemtCallbacks){
            _invokeFrameCallback(callback, _currentFrameTimeStamp);
        }
        ...
    }
    
    
    @RenderBing
    void drawFrame(){
        //布局
        pipelineOwner.flushLayout();
        //合成
        pipelineOwner.flushCompositingBits();
        //绘制
        pipelineOwner.flustPaint();
        render.conpositeFrame();
        pipelineOwner.flushSemantics();
    }
    
    void flushLayout(){
        //scheduleInitialLayout()@RenderView的时候,RenderView将自己加入了PipelineOwner._nodesNeedingLayout
        //遍历_nodesNeedingLayout
        node._layoutWidhResize();
    }
    
    @RenderObject
    void _layoutWidhResize(){
        performLayout();
        ...
    }
    
    @RenderView
    void performLayout(){
        //对于RenderView来说,其Child就是在挂载RenderObject过程中挂载的,
        //在这里是RenderParagraph
        child.layout(BoxConstraints.tight(_size);
    }
    
    @RenderObject
    void layout(Constraints constraints, {bool parentUserSize=false}){
        RenderObject relayoutBoundary;
        //this是RenderParagraph
        relayoutBoundary = this
        //根据子类具体实现,也是一般推荐子类只实现performLayout()的理由
        performLayout();
    }
    
    @RenderParagraph
    void performLayout(){
        _layoutChildern(constraints);
        _LayoutTextWithConstraints(constraints);
    }
    
    void _layoutChildren(){
        RenderBox child = firstChild;
        whild(child !=null){
            //遍历执行子类的layout
            child.layout(...);
            child = childAfter(child);
        }
    }
    

    在RenderObject树进行向下遍历Layout后,layout的操作就算完成了。

    @PipelineOwner
    voud flushPaint(){
        PaintingContext.repaintCompositedChild(node);
    }
    
    @PaintingContext
    static void repaintCompositedChild(RenderObject child, {bool debugAlsoPaintParent = false}){
        //同理,这里的child是RenderView
        _repaintCompositeChild(child);
    }
    
    static void _repaintCompositedChild(Render child){
        child._paintWithContext(childContext, Offset.zero);
        
    }
    @RenderObject
    void _paintWithContext(PaintingContext context, Offset offset){
        paint(context, offset);
    }
    
    @RenderView
    void paint(PaintingContext context, Offset offset){
        //在这里,child就是RenderParagraph
        context.paintChild(child,offset);
    }
    
    @PaintingContext
    void paintChild(RenderObject child, Offset offset){
        //在这里,child就是RenderParagraph,同上,会到底调用paint()
        child._paintWithContext(child,offset);
    }
    
    @RenderParagraph
    void paint(PaintingContext context, Offset offset){
        //自身的绘制逻辑
        while(child != null && childIndex < _textPainter.inlinePlaceholderBoxes.length){
            //执行child,递归调用
            context.paintChild(child.offset);
        }
    }
    

    于是,会递归RenderObject树,进行perform(),再递归RenderObject,进行paint();
    绘制完成后就可以调用engine进行绘制了。

    相关文章

      网友评论

          本文标题:flutter_run_app_analyze

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