Flutter框架分析分析系列文章:緩存
《Flutter框架分析(一)-- 總覽和Window》bash
《Flutter框架分析(二)-- 初始化》markdown
《Flutter框架分析(三)-- Widget,Element和RenderObject》app
《Flutter框架分析(四)-- Flutter框架的運行》框架
上篇文章《Flutter框架分析(一)-- 總覽和Window》介紹了Flutter框架最核心的渲染流水線和最基礎的Window
。這篇文章裏,咱們從Flutter框架的初始化來進入,來一步步揭開Flutter的面紗。寫過Flutter程序的同窗都知道,Flutter app的入口就是函數runApp()
。佈局
void main() {
runApp(MyApp());
}
複製代碼
那麼咱們就從函數runApp()
入手,看看這個函數被調用之後發生了什麼。post
runApp()
的函數體位於widgets/binding.dart
。長這樣:
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
..attachRootWidget(app)
..scheduleWarmUpFrame();
}
複製代碼
從調用的函數名稱就能夠看出來,它作了3件事,
WidgetsFlutterBinding
被初始化。OK,那咱們就來挨個看一下這3件事具體都作了什麼。
首先咱們先看一下WidgetsFlutterBinding
是什麼,從這個類的名稱來看,是把Widget和Flutter綁定在一塊兒的意思。
class WidgetsFlutterBinding extends BindingBase with GestureBinding, ServicesBinding, SchedulerBinding, PaintingBinding, SemanticsBinding, RendererBinding, WidgetsBinding {
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding.instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
}
複製代碼
這個類繼承自BindingBase
而且混入(Mixin
)了不少其餘類,看名稱都是不一樣功能的綁定。而靜態函數ensureInitialized()
所作的就是返回一個WidgetsBinding.instance
單例。
混入的那些各類綁定類也都是繼承自抽象類BindingBase
的。
abstract class BindingBase {
BindingBase() {
...
initInstances();
...
}
...
ui.Window get window => ui.window;
}
複製代碼
關於抽象類BindingBase
,你須要瞭解兩個地方,一個是在其構造的時候會調用函數initInstances()
。這個函數會由其子類,也就是上面說那些各類混入(Mixin)的綁定類各自實現,具體的初始化都是在其內部實現的。另外一個就是BindingBase
有一個getter
,返回的是window
。還記得在《Flutter框架分析(一)-- 總覽和Window》中提到過的窗口嗎?沒錯,這裏的window
就是它。那咱們是否是能夠推論,這些個綁定其實就是對window
的封裝?來,讓咱們挨個看一下這幾個綁定類在調用initInstances()
的時候作了什麼的吧。
第一個是GestureBinding
。手勢綁定。
mixin GestureBinding on BindingBase implements HitTestable, HitTestDispatcher, HitTestTarget {
@override
void initInstances() {
super.initInstances();
_instance = this;
window.onPointerDataPacket = _handlePointerDataPacket;
}
複製代碼
在調用initInstances()
的時候,主要作的事情就是給window
設置了一個手勢處理的回調函數。因此這個綁定主要是負責管理手勢事件的。
第二個是ServicesBinding
。服務綁定
mixin ServicesBinding on BindingBase {
@override
void initInstances() {
super.initInstances();
_instance = this;
window
..onPlatformMessage = BinaryMessages.handlePlatformMessage;
initLicenses();
}
複製代碼
這個綁定主要是給window
設置了處理Platform Message的回調。
第三個是SchedulerBinding
。調度綁定。
mixin SchedulerBinding on BindingBase, ServicesBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
window.onBeginFrame = _handleBeginFrame;
window.onDrawFrame = _handleDrawFrame;
SystemChannels.lifecycle.setMessageHandler(_handleLifecycleMessage);
}
複製代碼
這個綁定主要是給window
設置了onBeginFrame
和onDrawFrame
的回調,回憶一下上一篇文章講渲染流水線的時候,當Vsync信號到來的時候engine會回調Flutter的來啓動渲染流程,這兩個回調就是在SchedulerBinding
管理的。
第四個是PaintingBinding
。繪製綁定。
mixin PaintingBinding on BindingBase, ServicesBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
_imageCache = createImageCache();
}
複製代碼
這個綁定只是建立了個圖片緩存,就不細說了。
第五個是SemanticsBinding
。輔助功能綁定。
mixin SemanticsBinding on BindingBase {
@override
void initInstances() {
super.initInstances();
_instance = this;
_accessibilityFeatures = window.accessibilityFeatures;
}
複製代碼
這個綁定管理輔助功能,就不細說了。
第六個是RendererBinding
。渲染綁定。這是比較重要的一個類。
mixin RendererBinding on BindingBase, ServicesBinding, SchedulerBinding, GestureBinding, SemanticsBinding, HitTestable {
@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);
_mouseTracker = _createMouseTracker();
}
複製代碼
這個綁定是負責管理渲染流程的,初始化的時候作的事情也比較多。 首先是實例化了一個PipelineOwner
類。這個類負責管理驅動咱們以前說的渲染流水線。隨後給window
設置了一系列回調函數,處理屏幕尺寸變化,亮度變化等。接着調用initRenderView()
。
void initRenderView() {
assert(renderView == null);
renderView = RenderView(configuration: createViewConfiguration(), window: window);
renderView.scheduleInitialFrame();
}
複製代碼
這個函數實例化了一個RenderView
類。RenderView
繼承自RenderObject
。咱們都知道Flutter框架中存在這一個渲染樹(render tree)。這個RenderView
就是渲染樹(render tree)的根節點,這一點能夠經過打開"Flutter Inspector"看到,在"Render Tree"這個Tab下,最根部的紅框裏就是這個RenderView
。
最後調用addPersistentFrameCallback
添加了一個回調函數。請你們記住這個回調,渲染流水線的主要階段都會在這個回調裏啓動。
第七個是WidgetsBinding
,組件綁定。
mixin WidgetsBinding on BindingBase, SchedulerBinding, GestureBinding, RendererBinding, SemanticsBinding {
@override
void initInstances() {
super.initInstances();
_instance = this;
buildOwner.onBuildScheduled = _handleBuildScheduled;
window.onLocaleChanged = handleLocaleChanged;
window.onAccessibilityFeaturesChanged = handleAccessibilityFeaturesChanged;
SystemChannels.navigation.setMethodCallHandler(_handleNavigationInvocation);
SystemChannels.system.setMessageHandler(_handleSystemMessage);
}
複製代碼
這個綁定的初始化先給buildOwner
設置了個onBuildScheduled
回調,還記得渲染綁定裏初始化的時候實例化了一個PipelineOwner
嗎?這個BuildOwner
是在組件綁定裏實例化的。它主要負責管理Widget的重建,記住這兩個"owner"。他們將會Flutter框架裏的核心類。接着給window
設置了兩個回調,由於和渲染關係不大,就不細說了。最後設置SystemChannels.navigation
和SystemChannels.system
的消息處理函數。這兩個回調一個是專門處理路由的,另外一個是處理一些系統事件,好比剪貼板,震動反饋,系統音效等等。
至此,WidgetsFlutterBinding.ensureInitialized()
就跑完了,整體上來說是把window
提供的API分別封裝到不一樣的Binding裏。咱們須要重點關注的是SchedulerBinding
,RendererBinding
和WidgetsBinding
。這3個是渲染流水線的重要存在。
接下來就該看一下runApp()
裏的第二個調用了。
這個函數的代碼以下:
void attachRootWidget(Widget rootWidget) {
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget
).attachToRenderTree(buildOwner, renderViewElement);
}
複製代碼
在以前說的RendererBinding
的初始化的時候,咱們獲得了一個RenderView
的實例,render tree的根節點。RenderView
是繼承自RenderObject
的,而RenderObject
須要有對應的Widget
和Element
。上述代碼中的RenderObjectToWidgetAdapter
就是這個Widget
。而對應的Element
就是RenderObjectToWidgetElement
了,既然是要關聯到render tree的根節點,那它天然也就是element tree的根節點了。
從上述分析咱們能夠得出結論:
RendererBinding
)經過pipelineOwner
間接持有render tree的根節點RenderView
。WidgetsBinding
)持有element tree的根節點RenderObjectToWidgetElement
。那麼RenderObjectToWidgetElement
是怎麼和RenderView
關聯起來的呢,那天然是經過一個Widget作到的了,看下RenderObjectToWidgetAdapter
的代碼:
class RenderObjectToWidgetAdapter<T extends RenderObject> extends RenderObjectWidget {
/// Creates a bridge from a [RenderObject] to an [Element] tree.
///
/// Used by [WidgetsBinding] to attach the root widget to the [RenderView].
RenderObjectToWidgetAdapter({
this.child,
this.container,
this.debugShortDescription
}) : super(key: GlobalObjectKey(container));
@override
RenderObjectToWidgetElement<T> createElement() => RenderObjectToWidgetElement<T>(this);
@override
RenderObjectWithChildMixin<T> createRenderObject(BuildContext context) => container;
...
}
複製代碼
你看,createElement()
返回的就是RenderObjectToWidgetElement
,而createRenderObject
返回的container
就是構造這個Widget傳入的RenderView
了。而咱們本身的MyApp
做爲一個子widget存在於RenderObjectToWidgetAdapter
之中。
最後調用的attachToRenderTree
作的事情屬於咱們以前說的渲染流水線的構建(Build)階段,這時會根據咱們本身的widget生成element tree和render tree。構建(Build)階段完成之後,那天然是要進入佈局(Layout)階段和繪製(Paint)階段了。怎麼進呢?那就是runApp
裏的最後一個函數調用了。
void scheduleWarmUpFrame() {
...
Timer.run(() {
...
handleBeginFrame(null);
...
});
Timer.run(() {
...
handleDrawFrame();
...
});
}
複製代碼
這個函數其實就調了兩個函數,就是以前咱們講window
的時候說的兩個回調函數onBeginFrame
和onDrawFrame
嗎?這裏其實就是在具體執行這兩個回調。最後渲染出來首幀場景送入engine顯示到屏幕。這裏使用Timer.run()
來異步運行兩個回調,是爲了在它們被調用以前有機會處理完微任務隊列(microtask queue)。關於Dart代碼異步執行能夠參考個人文章《Flutter/Dart中的異步》
咱們以前說渲染流水線是由Vsync信號驅動的,可是上述過程都是在runApp()
裏完成的。並無看到什麼地方告訴engine去調度一幀。這是由於咱們是在作Flutter的初始化。爲了節省等待Vsync信號的時間,因此就直接把渲染流程跑完作出來第一幀圖像來了。
Flutter框架的初始化就介紹完了。順帶還包括了Flutter app首幀渲染的一個大體流程。本文中所說的Flutter框架初始化過程其實主要的點都在幾個綁定(binding)的初始化。理解的時候要記住上篇文章中介紹的渲染流水線和window
。Flutter框架其實就是圍繞這兩個東西在作文章。總結起來本文的要點這麼幾個:
SchedulerBinding
,RendererBinding
和WidgetsBinding
。PipelineOwner
和BuildOwner
。RenderView
;element tree根節點RenderObjectToWidgetElement
。有了這些基礎之後,後續的文章咱們會再去分析Widget
,Element
和RenderObject
之間的關係,以及具體的Flutter渲染流水線各階段是如何工做的。