Flutter?runApp到渲染上屏分析詳解
起源
flutter作為一個(gè)跨平臺(tái)的框架,在繪制上體現(xiàn)出了它跨平臺(tái)的良好性能.那么,它是如何從runApp()后 繪制上屏的呢?本文將與你一起去探索這一過(guò)程.
ps: 為了思維不中斷, 本文僅對(duì)整體流程作分析,不會(huì)深入分析具體實(shí)現(xiàn)
我們運(yùn)行一個(gè)flutter app ,入口一定是從runApp() 中進(jìn)行的. 那么flutter 在runApp() 中做了哪些處理呢? 首先,我們從runApp() 這個(gè)函數(shù)聊起.它是一個(gè)需要傳入Widget 的函數(shù).而傳入的Widget ,即首屏渲染所需的Widget.
在此我們應(yīng)該知道這個(gè)概念, 即widget 是flutter 中用來(lái)描述ui如何繪制的配置文件,去形容一個(gè)組件在整體中的位置、大小.
那么不難推斷出.在runApp() 的過(guò)程中,如果Widget是繪制的配置文件. 那么手勢(shì)注冊(cè)、楨調(diào)度等都應(yīng)該是在此時(shí)注冊(cè)的. 帶著這樣的推斷我們?nèi)ピ创a中找答案.
分析準(zhǔn)備
void runApp(Widget app) {
WidgetsFlutterBinding.ensureInitialized()
// 這里解釋下:
// ..是flutter中的級(jí)聯(lián)運(yùn)算符
// 可以同一個(gè)對(duì)象上連續(xù)調(diào)用多個(gè)對(duì)象的變量或方法
..scheduleAttachRootWidget(app)
..scheduleWarmUpFrame();
}
在runApp() 中可以看到, 這里實(shí)際上也就是調(diào)用了三個(gè)方法,以下我們對(duì)每個(gè)方法進(jìn)行刨析.
ensureInitialized
從字面意思看,這是為了確保已經(jīng)初始化而調(diào)用的方法.它的作用是為了返回WidgetsBinding的對(duì)象.如果了解單列模式的話,會(huì)發(fā)現(xiàn)這么寫(xiě)實(shí)際上就是一個(gè)單列模式.
static WidgetsBinding ensureInitialized() {
if (WidgetsBinding._instance == null)
WidgetsFlutterBinding();
return WidgetsBinding.instance;
}
這里我們?nèi)ネ诰蛞幌耊idgetsFlutterBinding內(nèi)部的構(gòu)造函數(shù)在初始化時(shí)做了什么? 它是繼承BingingBase的, 我們進(jìn)入BingingBase中淺看一下大概的實(shí)現(xiàn), Timeline 和assert這部分代碼我們可以忽略.
ps: assert 在release代碼中不會(huì)執(zhí)行
也就是實(shí)際上的結(jié)構(gòu)是這樣的
BindingBase() {
initInstances();
initServiceExtensions();
}
- initInstances() 方法是為了綁定初始化實(shí)例和其他的一些狀態(tài).
- initServiceExtensions() 方法是為了綁定初始化服務(wù)
這里我們回過(guò)頭來(lái)看WidgetsFlutterBinding它的一些實(shí)現(xiàn)接口, 順序依次是:
| 接口 | 解釋 |
|---|---|
| GestureBinding | 實(shí)現(xiàn)點(diǎn)擊命中測(cè)試 |
| SchedulerBinding | 引入了幀的概念 |
| ServicesBinding | 提供對(duì)插件的訪問(wèn) |
| PaintingBinding | 解碼圖像 |
| SemanticsBinding | 語(yǔ)義樹(shù) |
| RendererBinding | 處理render tree |
| WidgetsBinding | 處理widget tree |
內(nèi)部的具體實(shí)現(xiàn)這里不再贅述,后續(xù)會(huì)逐章對(duì)這些進(jìn)行分解、解釋.這里只是去分析整體的流程. 也就是說(shuō), 在這里我們完成了對(duì)app系統(tǒng)的初始化、推動(dòng)界面的繪制,獲取手勢(shì)等等.
scheduleAttachRootWidget
在一系列服務(wù)注冊(cè)完之后,我們需要把當(dāng)前的root widget 掛載到樹(shù)上,
void scheduleAttachRootWidget(Widget rootWidget) {
Timer.run(() {
attachRootWidget(rootWidget);
});
}
在attachRootWidget 的方法中, 我們構(gòu)建了RenderObjectToWidgetAdapter 的對(duì)象. 通過(guò)RenderObjectToWidgetAdapter 當(dāng)作Element 和RenderObject 之間的橋梁,
{
final bool isBootstrapFrame = renderViewElement == null;
_readyToProduceFrames = true;
_renderViewElement = RenderObjectToWidgetAdapter<RenderBox>(
container: renderView,
debugShortDescription: '[root]',
child: rootWidget,
).attachToRenderTree(buildOwner!, renderViewElement as RenderObjectToWidgetElement<RenderBox>?);
if (isBootstrapFrame) {
SchedulerBinding.instance.ensureVisualUpdate();
}
}
同時(shí),這里 根據(jù)renderViewElement 有沒(méi)有賦值來(lái)判斷是否是第一次加載.如果是第一次加載頁(yè)面,會(huì)通知界面去刷新ui
scheduleWarmUpFrame
這個(gè)方法從字面意思來(lái)看 應(yīng)該是在界面啟動(dòng)時(shí)去執(zhí)行的一些方法. 首先,我們看一下它的一些引用路徑.發(fā)現(xiàn)一共有三個(gè)地方的代碼都引用了這個(gè)方法

可以看到調(diào)用的三個(gè)地方分別是:
- allowFirstFrame()
- performReassemble()
- runApp()
好家伙,這不都是類(lèi)似于繪制的入口函數(shù)? 因此,我們可以推斷這里就是一些繪制時(shí)初始化時(shí)候必須執(zhí)行的一些代碼.
{
// 這里通過(guò)偽代碼簡(jiǎn)要了解一下大致實(shí)現(xiàn)
//
if (_warmUpFrame || schedulerPhase != SchedulerPhase.idle) return;
_warmUpFrame = true;
// 這里把源代碼提前了,由于handle*()的代碼都是通過(guò)Timer.run執(zhí)行的,實(shí)際上是
// 一種異步執(zhí)行,會(huì)在下一幀去調(diào)用
lockEvents(() async {
await endOfFrame;
timelineTask.finish();
});
// 開(kāi)始幀回調(diào)
handleBeginFrame(null);
// 新幀回調(diào)處理
handleDrawFrame();
// 這里和熱重載相關(guān)
resetEpoch();
_warmUpFrame = false;
//
if (hadScheduledFrame) scheduleFrame();
}
總結(jié)
總結(jié)一下, runApp() 通過(guò)
- 注冊(cè)各種服務(wù)
- 注冊(cè)u(píng)i
- 繪制上屏
最終在屏幕上呈現(xiàn)出ui,其中還有如PipelineOwner、BuildOwner等等非常重要的api,這里暫且不表. 后續(xù)我們單章詳細(xì)介紹,希望這一次與你一起閱讀的思路可以幫你一起思考啟動(dòng)的流程.
以上就是Flutter runApp到渲染上屏分析詳解的詳細(xì)內(nèi)容,更多關(guān)于Flutter runApp渲染上屏的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android開(kāi)發(fā)之無(wú)痕過(guò)渡下拉刷新控件的實(shí)現(xiàn)思路詳解
下拉刷新效果功能在程序開(kāi)發(fā)中經(jīng)常會(huì)見(jiàn)到,今天小編抽時(shí)間給大家分享Android開(kāi)發(fā)之無(wú)痕過(guò)渡下拉刷新控件的實(shí)現(xiàn)思路詳解,需要的朋友參考下吧2016-11-11
Kotlin?Flow數(shù)據(jù)流的3種使用場(chǎng)景詳解
這篇文章主要為大家詳細(xì)介紹了Kotlin中Flow數(shù)據(jù)流的幾種使用場(chǎng)景,文中的示例代碼講解詳細(xì),具有一定的借鑒價(jià)值,需要的可以參考一下2023-04-04
Android實(shí)現(xiàn)檢測(cè)實(shí)體按鍵事件并屏蔽
這篇文章主要介紹了Android實(shí)現(xiàn)檢測(cè)實(shí)體按鍵事件并屏蔽 ,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08
Android中的android:layout_weight使用詳解
layout_weight的作用是設(shè)置子空間在LinearLayout的重要度(控件的大小比重)。layout_weight的值越低,則控件越重要,下面為大家介紹下具體的使用方法2013-06-06
Android中判斷是否聯(lián)網(wǎng)實(shí)現(xiàn)代碼
這篇文章主要介紹了Android中判斷是否聯(lián)網(wǎng)實(shí)現(xiàn)代碼,本文直接給出實(shí)現(xiàn)代碼,需要的朋友可以參考下2015-06-06
RecyclerView實(shí)現(xiàn)橫向滾動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了RecyclerView實(shí)現(xiàn)橫向滾動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-01-01
Android7.0 工具類(lèi):DiffUtil詳解
這篇文章主要介紹了Android7.0 工具類(lèi):DiffUtil的相關(guān)資料,并附實(shí)例代碼,和實(shí)現(xiàn)效果圖,需要的朋友可以參考下2016-09-09
淺談Android PathMeasure詳解和應(yīng)用
本篇文章主要介紹了淺談Android PathMeasure詳解和應(yīng)用,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-01-01
Android組件DrawerLayout仿網(wǎng)易新聞v4.4側(cè)滑菜單
這篇文章主要為大家詳細(xì)介紹了Android組件DrawerLayout仿網(wǎng)易新聞v4.4側(cè)滑菜單,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01

