flutter 繪制過程 系列1-Binding

1、Widget

StatelessWidget和StatefulWidget都繼承自Widget。

Widget作為虛類,定義了Element createElement()方法,給繼承者實(shí)現(xiàn),返回Element對象。

具體到StatelessWidget,實(shí)現(xiàn)createElement,返回StatelessElement對象。StatelessElement繼承自ComponentElement。

具體到StatefulWidget,實(shí)現(xiàn)createElement,返回StatefulElement對象。StatefulElement繼承自ComponentElement。

ComponentElement繼承自Element類。

在執(zhí)行createElement方法時,都會把Widget傳遞給Element對象,因此Element持有一個Widget對象。

2、Element

在Element樹的特定位置,Element代表一個Widget實(shí)例。

多個Element形成了一顆Element樹,大多數(shù)Element有一個獨(dú)一無二的child,但是那些繼承自RenderObjectElement的Element,就可以有多個child.

通過調(diào)用Widget.createElement方法創(chuàng)建一個Element,通過調(diào)用Element的mount方法,將一個新的Element添加到一個父Element的屬性為slot的位置。

Element類的mount方法,負(fù)責(zé)填充所有的子Widget,并在必要時調(diào)用attachRenderObject方法,將關(guān)聯(lián)的渲染對象(render objects)附著到渲染樹,此時Element被標(biāo)記為“active”,然后在屏幕上顯示。

3、RenderObject

渲染樹中的一個對象,通過RenderObjectToWidgetAdapter將其和Element聯(lián)系起來。渲染樹的根是RenderView,他有一個唯一的child,是RenderBox類型。在繪制階段,將RenderObject生成對應(yīng)的Layer tree,再將其生成Scene,交給GPU繪制。

總結(jié)

三者之間的基本關(guān)系就是:Element持有Widget對象,在Element的mount階段,通過Widget對象創(chuàng)建RenderObject對象,這個對象被Element持有。所以Element持有Widget和RenderObject對象。

Element管理著Widget生命周期,在生命周期不同階段,處理RenderObject不同的渲染繪制任務(wù)。

4、啟動

首先從main.dart的main方法開始運(yùn)行:

void main() {
  runApp(MyApp());
}

runApp方法是在一個binding.dart文件里面:

packages\flutter\lib\src\widgets\binding.dart

void runApp(Widget app) {
  WidgetsFlutterBinding.ensureInitialized()
    ..scheduleAttachRootWidget(app)
    ..scheduleWarmUpFrame();
}

4.1 WidgetsFlutterBinding

packages\flutter\lib\src\widgets\binding.dart

WidgetsFlutterBinding類調(diào)用靜態(tài)初始化方法,執(zhí)行初始化操作:

class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
  static WidgetsBinding ensureInitialized() {
    if (WidgetsBinding.instance == null)
      WidgetsFlutterBinding();
    return WidgetsBinding.instance;
  }
}

with關(guān)鍵字類似于java里面的implement關(guān)鍵字,可以初始化及調(diào)用with后面類的方法。如果對于這個關(guān)鍵字比較陌生的話,可以寫下面的demo代碼驗(yàn)證一下,看看初始化順序是怎么樣的:

abstract class Test1{
  Test1(){
    print("parent class");
    init();
  }

  void init(){
    print("class init");
  }
}

mixin Demo1 on Test1{
  void init(){
    print("demo1 init");
    super.init();
    print("demo1 init done");
  }
}

mixin Demo2 on Test1,Demo1 {
  void init(){
    print("demo2 init");
    super.init();
    print("demo2 init done");
  }
}

class Test2 extends Test1 with Demo1,Demo2{
  Test2(){
    print("child class");
  }
  
  static void test(){
    Test2();
  }
}

void main() {
  Test2.test();
}

parent class

demo2 init

demo1 init

class init

demo1 init done

demo2 init done

child class

透過demo我們可以知道,調(diào)用順序是先調(diào)用父類構(gòu)造函數(shù),然后with后面的類,從后往前調(diào)用,但是因?yàn)樵诿總€binding類的initInstances方法中,都先調(diào)用了super.initInstances方法,所以實(shí)際上先執(zhí)行前面的binding類代碼。先看看BindingBase的構(gòu)造函數(shù):

packages\flutter\lib\src\foundation\binding.dart

BindingBase() {
    initInstances();
}

到這里開始執(zhí)行with從前到后binding的initInstances方法。

4.2 GestureBinding

綁定手勢子系統(tǒng),當(dāng)一個點(diǎn)按下事件從window傳遞過來之后,被其攔截,由GestureBinding決定在哪一個節(jié)點(diǎn)生效。

@override
void initInstances() {
    super.initInstances();
    _instance = this;
    window.onPointerDataPacket = _handlePointerDataPacket;
}

獲取window窗口的onPointerDataPacket方法。

4.3 ServicesBinding

監(jiān)聽系統(tǒng)消息,并通過BinaryMessenger轉(zhuǎn)發(fā)。

@override
void initInstances() {
    super.initInstances();
    _instance = this;
    _defaultBinaryMessenger = createBinaryMessenger();
    window
      ..onPlatformMessage = defaultBinaryMessenger.handlePlatformMessage;
    initLicenses();
    SystemChannels.system.setMessageHandler(handleSystemMessage);
}

通過createBinaryMessenger方法創(chuàng)建了一個默認(rèn)的消息傳遞者。

4.4 SchedulerBinding

調(diào)度管理。調(diào)度分為幾個階段,分別是:

  • 沒有frame需要處理的時候,處于Idle。
  • transientCallbacks,暫時的回調(diào),比如更新RenderObject狀態(tài)到animate。
  • midFrameMicrotasks,中間幀微服務(wù),比如正在處理暫時回調(diào)任務(wù)的時候。
  • persistentCallbacks,持續(xù)性回調(diào),比如layer在創(chuàng)建,布局,繪制階段。
  • postFrameCallbacks,清理,并準(zhǔn)備下一幀。
@override
void initInstances() {
    super.initInstances();
    _instance = this;
    SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
    readInitialLifecycleStateFromNativeWindow();
    
    if (!kReleaseMode) {
      int frameNumber = 0;
      addTimingsCallback((List<FrameTiming> timings) {
        for (FrameTiming frameTiming in timings) {
          frameNumber += 1;
          _profileFramePostEvent(frameNumber, frameTiming);
        }
      });
    }
}

4.5 PaintingBinding

綁定了繪制庫。

hook了緩存清理邏輯,用于清理圖片緩存。

@override
void initInstances() {
    super.initInstances();
    _instance = this;
    _imageCache = createImageCache();
    if (shaderWarmUp != null) {
      shaderWarmUp.execute();
    }
}

初始化方法中創(chuàng)建了圖片緩存對象,設(shè)置默認(rèn)圖片緩存大小。

shaderWarmUp其實(shí)是一個著色器預(yù)處理。在正式運(yùn)行app之前,先生成一個著色器ShaderWarmUp,繪制一個場景(Scene)到里面,并緩存起來。當(dāng)app里面有復(fù)雜的場景需要著色時,可以減少動畫或交互時的卡頓。

著色預(yù)熱操作是在GPU線程里面同步操作的,意味著,app第一幀的渲染必須等到該操作完成之后才能繼續(xù)。

4.6 SemanticsBinding

Semantics是語義的意思。這個類將語義層(semantics layer)與flutter engine聯(lián)系起來。

@override
void initInstances() {
    super.initInstances();
    _instance = this;
    _accessibilityFeatures = window.accessibilityFeatures;
}

4.7 RendererBinding

packages\flutter\lib\src\rendering\binding.dart

將渲染樹和Flutter engine聯(lián)系起來。

@override
void initInstances() {
    super.initInstances();
    _instance = this;
    _pipelineOwner = PipelineOwner(
      onNeedVisualUpdate: ensureVisualUpdate,
      onSemanticsOwnerCreated: _handleSemanticsOwnerCreated,
      onSemanticsOwnerDisposed: _handleSemanticsOwnerDisposed,
    );
    window
      ..onMetricsChanged = handleMetricsChanged
      ..onTextScaleFactorChanged = handleTextScaleFactorChanged
      ..onPlatformBrightnessChanged = handlePlatformBrightnessChanged
      ..onSemanticsEnabledChanged = _handleSemanticsEnabledChanged
      ..onSemanticsAction = _handleSemanticsAction;
    initRenderView();
    _handleSemanticsEnabledChanged();
    assert(renderView != null);
    addPersistentFrameCallback(_handlePersistentFrameCallback);
    initMouseTracker();
}

看看幾個關(guān)鍵的類。

4.7.1 PipelineOwner

packages\flutter\lib\src\rendering\object.dart

PipelineOwner管理著渲染流程。

PipelineOwner對外提供接口用于驅(qū)動渲染流程。并存儲著那些請求訪問渲染流程中各個階段的狀態(tài)??梢酝ㄟ^以下步驟來刷新渲染流程:

  • flushLayout

更新任何需要重新計(jì)算布局的渲染對象。在這個階段渲染對象的大小和布局都被計(jì)算好了。在此階段,渲染對象的繪制和合成狀態(tài)被置成dirty。

  • flushCompositingBits

渲染對象的復(fù)合bit被設(shè)置成dirty狀態(tài)之后會在這個階段更新,并且每一個渲染對象都可以知道自己的子對象是否需要被復(fù)合。這個信息在繪制階段執(zhí)行裁剪的時候會起到作用。

  • flushPaint

訪問那些需要被繪制的渲染對象。在這個階段,這些渲染對象有機(jī)會記錄繪制命令到PictureLayer,以及組建其他復(fù)合Layer。

  • flushSemantics

最后,如果啟用了語義,該階段將編譯渲染對象的語義。 該語義信息由輔助技術(shù)可改善渲染樹的可訪問性。

4.7.2 RenderView

接下來會執(zhí)行initRenderView方法,創(chuàng)建一個RenderView對象,他繼承自RenderObject類,是整個渲染樹的根。

void initRenderView() {
    renderView = RenderView(configuration: createViewConfiguration(), window: window);
    renderView.prepareInitialFrame();
}

接下來RenderView的對象還調(diào)用了prepareInitialFrame方法,如下:

packages\flutter\lib\src\rendering\view.dart

void prepareInitialFrame() {
    scheduleInitialLayout();
    scheduleInitialPaint(_updateMatricesAndCreateNewRootLayer());
}
  • scheduleInitialLayout方法:

packages\flutter\lib\src\rendering\object.dart

void scheduleInitialLayout() {
    _relayoutBoundary = this;
    owner._nodesNeedingLayout.add(this);
}

這個方法屬于RenderObject類,由此我們看到RenderView實(shí)際調(diào)用的是父類的方法,而這個owner就是我們在initInstances方法中初始化的PipelineOwner類的對象。

_nodesNeedingLayout是一個RenderObject對象的List,全局已經(jīng)初始化了,在這里先將RenderView對象添加進(jìn)去,作為根。

  • _updateMatricesAndCreateNewRootLayer方法

在調(diào)用scheduleInitialPaint方法之前先調(diào)用_updateMatricesAndCreateNewRootLayer新建一個Layer對象:

packages\flutter\lib\src\rendering\view.dart

Layer _updateMatricesAndCreateNewRootLayer() {
    _rootTransform = configuration.toMatrix();
    final ContainerLayer rootLayer = TransformLayer(transform: _rootTransform);
    rootLayer.attach(this);
    return rootLayer;
}

ContainerLayer其實(shí)是一個復(fù)合的Layer,可以包含很多子Layer。另外Layer繼承自AbstractNode。

  • scheduleInitialPaint方法:

packages\flutter\lib\src\rendering\object.dart

void scheduleInitialPaint(ContainerLayer rootLayer) {
    _layer = rootLayer;
    owner._nodesNeedingPaint.add(this);
}

_nodesNeedingPaint是一個RenderObject對象的List,全局已經(jīng)初始化了,在這里先將一個RenderView對象添加進(jìn)去,另外可以看到RenderObject持有一個Layer對象。

4.7.3 addPersistentFrameCallback方法

回到RendererBinding的initInstances方法中,addPersistentFrameCallback方法添加回調(diào),回調(diào)方法是_handlePersistentFrameCallback。

addPersistentFrameCallback方法則是把這個回調(diào)放到List中存起來,在SchedulerBinding的_persistentCallbacks階段集中調(diào)用:

final List<FrameCallback> _persistentCallbacks = <FrameCallback>[];

void addPersistentFrameCallback(FrameCallback callback) {
    _persistentCallbacks.add(callback);
}

_handlePersistentFrameCallback方法里面調(diào)用的就是drawFrame方法。

4.8 WidgetsBinding

packages\flutter\lib\src\widgets\binding.dart

將widget layer與flutter engine融合在一起。

initInstances方法:

packages\flutter\lib\src\widgets\binding.dart

void initInstances() {
    super.initInstances();
    _buildOwner = BuildOwner();
    buildOwner.onBuildScheduled = _handleBuildScheduled;
}

BuildOwner類管理著widget的框架,比如跟蹤哪些widget需要重建,在全局處理應(yīng)用于widget樹的任務(wù),比如開發(fā)者在調(diào)試時啟動熱加載,就會組織那些渲染樹中不活躍的列表元素,并觸發(fā)重組指令。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容