flutter_run_app_analyze

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进行绘制了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352