最近參與了Flutter項(xiàng)目模塊的開(kāi)發(fā)工作区宇,同時(shí)很好奇Flutter內(nèi)部的原理是什么,于是做了些研究议谷;Flutter是一種“響應(yīng)式框架”,與React類似芬首;開(kāi)發(fā)人員面向的是Widget,相當(dāng)于View衩辟,沒(méi)有Activity或者ViewController的概念;再深入探索昼钻,Widget其實(shí)只是負(fù)責(zé)配置數(shù)據(jù)封寞,真正的處理渲染是Element、RenderObject碗淌;在理解框架原理之后再使用Flutter確實(shí)會(huì)生動(dòng)很多抖锥。
一、框架
還是先從flutter的架構(gòu)入手纳像,它分為Framework層和Engine層兩部分拯勉,其中Framework用的是Dart語(yǔ)言,Engine是C++宫峦,可以理解為Engine是Framework和Android/iOS的橋梁;先來(lái)簡(jiǎn)單看一下犀勒,分析上面的架構(gòu)圖妥曲,F(xiàn)ramework層有兩種樣式規(guī)則Material(Android)、Cupertino(iOS)逾一,開(kāi)發(fā)人員只需關(guān)心Widgets(配置信息)遵堵,在內(nèi)部進(jìn)行渲染怨规,其中包括動(dòng)畫(huà)锡足、布局、繪制等舶得,將渲染后的圖層送入Engine,Engine包括Skia(渲染引擎)文字處理引擎等纫骑。
二九孩、渲染流水線
接下來(lái)看下它的渲染流水線,GPU發(fā)送Vsync(垂直同步信號(hào))通過(guò)Engine層煤墙,發(fā)送給Framework宪拥,F(xiàn)ramework接收到信號(hào)進(jìn)行動(dòng)畫(huà)、構(gòu)建设预、布局犁河、繪制順序依次進(jìn)行魄梯,最終渲染成Layer Tree 發(fā)給GPU;
那么GPU發(fā)送Vsync的時(shí)機(jī)是什么呢酿秸?其實(shí)在狀態(tài)發(fā)生變化的時(shí)候(比如調(diào)用State.setState()),此時(shí)Framework層會(huì)發(fā)送Vsync請(qǐng)求給Engine肝箱,Engine接收到后告知系統(tǒng)稀蟋,系統(tǒng)會(huì)發(fā)送Vsync信號(hào)再次通過(guò)Engine傳遞給Framework,F(xiàn)ramework接收到信號(hào)后進(jìn)行渲染骏融。
三、布局渲染
接上面怀泊,Render tree來(lái)負(fù)責(zé)布局渲染误趴,過(guò)程中需要繪制RenderObject所有子樹(shù),所以我們看到的界面是由很多l(xiāng)ayer組合成的凉当,也就是layer tree。渲染機(jī)制是這樣的糯而,由GPU發(fā)送Vsync信號(hào)泊窘,通知Framework更新一幀,在UI線程中通過(guò)build烘豹、layout和paint渲染成Layer Tree携悯,不過(guò)此時(shí)的渲染結(jié)果仍是矢量描述數(shù)據(jù),怎么做才能轉(zhuǎn)成讓用戶看得見(jiàn)的直觀視圖呢憔鬼?這就要Engine通過(guò)“光柵化”將矢量描述數(shù)據(jù)生成一個(gè)個(gè)的像素填充數(shù)據(jù);之后傳入GPU線程組合Layer Tree昌跌,由Skia渲染引擎渲染后送給GPU展示照雁。
四、State生命周期
State由Statefulwidget創(chuàng)建維護(hù)萍诱,這里說(shuō)一下污呼,Statefulewidget不是RenderObjectWidget,也就是它只有Elment沒(méi)有RenderObject燕酷,所以它不能渲染對(duì)象映企,它的職責(zé)是維護(hù)State,接下來(lái)看下State的生命周期堰氓。
initState:初始化
build:經(jīng)過(guò)初始化準(zhǔn)備好State后苹享,通過(guò)build構(gòu)建,如statelessWidget的build囤攀,StatefulWidget State的bulid
deactivate:State從視圖樹(shù)暫時(shí)刪除宫纬,如頁(yè)面跳轉(zhuǎn)切換
dispose:State從視圖樹(shù)永久移除
didiUpdateConfig:Widget配置變化,如:熱修復(fù)
setState: 需要更新視圖蝌衔,主動(dòng)調(diào)這個(gè)函數(shù)
五蝌蹂、原理
Widget噩斟、Element、RenderObject密不可分的關(guān)系
在探索構(gòu)建孤个、布局及繪制原理前剃允,需要先了解Widget、Element齐鲤、RenderObject的原理及相互關(guān)系斥废。Widget會(huì)創(chuàng)建自己的Element和RenderObject(并不是每個(gè)Widget都有RenderObject,如statefulWidget给郊、statelessWidget)牡肉,其中Element持有Widget和RenderObject兩者的引用;開(kāi)發(fā)者面向的只有Widget丑罪,其中Widget是不可變的凤壁,Element吩屹、RenderObject是可變的,正是這樣大大提高的Flutter的性能拧抖。
可以理解Widget只是配置信息煤搜,實(shí)際重點(diǎn)工作都在Element中進(jìn)行,也就是build階段唧席,而RenderObject負(fù)責(zé)布局及繪制的工作擦盾;打個(gè)比方嘲驾,Widget是戰(zhàn)略部署人員,他只負(fù)責(zé)部署戰(zhàn)略迹卢,當(dāng)一套戰(zhàn)略部署需要修改的時(shí)候辽故,并不會(huì)在原有基礎(chǔ)上修改,而是再次制定新的方案腐碱,制定完成后將該方案交給實(shí)施方案的負(fù)責(zé)人(Element)誊垢,此時(shí)負(fù)責(zé)人將新方案與舊的方案加以比較,修改變動(dòng)點(diǎn)症见,將具體的方案流程交給(RenderObject)去實(shí)現(xiàn)喂走。
再看一下官方的一個(gè)例子:
首先Recanle green Widget下面有一個(gè)子Widget Circle blue,綠色組件創(chuàng)建一個(gè)Element谋作,再創(chuàng)建一個(gè)RenderObject芋肠,Element同時(shí)持有兩者的引用;接下來(lái)藍(lán)色組件創(chuàng)建一個(gè)Element遵蚜,并將其掛在到父Element上帖池,再創(chuàng)建一個(gè)RenderObject,attach到了父RenderObjuect的插槽谬晕,此時(shí)渲Render tree建立碘裕。
Element負(fù)責(zé)build階段,接下來(lái)看下Element Tree攒钳,Element構(gòu)建了Element Tree帮孔,它的職責(zé)就是維護(hù)這棵樹(shù),可以理解Element Tree是一個(gè)“虛擬DOM”不撑,由于DOM每次更新操作都會(huì)重新渲染整個(gè)DOM文兢,非常耗性能,所以出現(xiàn)了虛擬DOM的概念焕檬,也就是通過(guò)diff算法姆坚,只更新改變的元素就好,大大提升了效率实愚,Element Tree也在flutter中扮演了這樣的角色兼呵,如statefulWidget通過(guò)state.setState通知Element改變,Element對(duì)需要更新的Widget標(biāo)記為dirty狀態(tài)(被標(biāo)記dirty的Element會(huì)加到dirty list中)腊敲,表示這個(gè)Element需要被重建击喂。
RenderObject負(fù)責(zé)渲染,在build后碰辅,開(kāi)始布局及繪制(這里涉及到約束布局的概念懂昂,下面會(huì)說(shuō)一下),之后生成適量描述數(shù)據(jù)没宾,這部分就是RenderObject的工作凌彬。接下來(lái)就是上面提到過(guò)的光柵化及Skia處理后送往GPU沸柔。
布局約束
Flutter是盒子約束模型,上圖中的每個(gè)節(jié)點(diǎn)都是RenderObject铲敛,父節(jié)點(diǎn)會(huì)將約束傳達(dá)給下面的所有子節(jié)點(diǎn)褐澎,如最大最小寬高;子節(jié)點(diǎn)會(huì)依據(jù)約束定義具體size原探,將具體的size乱凿,如width:200、height:200咽弦,告訴父節(jié)點(diǎn)一直傳給頂部徒蟆。