成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

移動(dòng)開(kāi)發(fā)新利器 | 一文深入了解 Flutter 界面開(kāi)發(fā)

新聞 Android
談到移動(dòng)端開(kāi)發(fā),大家心中肯定會(huì)涌現(xiàn)出一系列名詞:iOS、Android、Weex,H5... 那為何還使用 Flutter?其實(shí),F(xiàn)lutter 通過(guò)自建繪制引擎,具備與 Native 媲美的性能指數(shù),且有很好的兩端一致性,因此 Flutter 提供了一種新的可選項(xiàng)。

 [[232114]]

  阿里妹導(dǎo)讀:談到移動(dòng)端開(kāi)發(fā),大家心中肯定會(huì)涌現(xiàn)出一系列名詞:iOS、Android、Weex,H5... 那為何還使用 Flutter?其實(shí),F(xiàn)lutter 通過(guò)自建繪制引擎,具備與 Native 媲美的性能指數(shù),且有很好的兩端一致性,因此 Flutter 提供了一種新的可選項(xiàng)。閑魚寶貝詳情頁(yè)實(shí)踐上線也證明了這點(diǎn),可以在性能無(wú)損前提下降低 iOS&Android 開(kāi)發(fā)成本。

  本文由閑魚技術(shù)團(tuán)隊(duì)出品。它將為你深入介紹 Flutter framework 關(guān)于視圖樹的創(chuàng)建與管理機(jī)制、布局、渲染的原理,以及 Flutter 布局與渲染相關(guān)性能優(yōu)化的設(shè)計(jì)思路的文章。同時(shí)介紹在使用 Flutter 開(kāi)發(fā)過(guò)程中,遇到的一些坑和相應(yīng)的解決方案。

  Flutter 框架簡(jiǎn)介

  1. 跨平臺(tái)應(yīng)用的框架,沒(méi)有使用 WebView 或者系統(tǒng)平臺(tái)自帶的控件,使用自身的高性能渲染引擎(Skia)自繪。

  2. 界面開(kāi)發(fā)語(yǔ)言使用 dart,底層渲染引擎使用C, C++。

  3. 組合大于繼承,控件本身通常由許多小型、單用途的控件組成,結(jié)合起來(lái)產(chǎn)生強(qiáng)大的效果,類的層次結(jié)構(gòu)是扁平的,以***化可能的組合數(shù)量。

  Rendering Pipeline

  本文主要介紹 build、layout、paint 的三個(gè)階段。

  視圖樹

  Widget&Element&RenderObject

  Flutter 視圖樹包含了三種樹,上圖只是介紹了三顆樹的基礎(chǔ) class 的對(duì)應(yīng)關(guān)系和功能介紹。

  創(chuàng)建樹

  1. 創(chuàng)建 widget 樹

  2. 調(diào)用 runApp (rootWidget),將 rootWidget 傳給 rootElement,做為 rootElement 的子節(jié)點(diǎn),生成 Element 樹,由 Element 樹生成 Render 樹

  • Widget:存放渲染內(nèi)容、視圖布局信息,widget 的屬性***都是 immutable (如何更新數(shù)據(jù)呢?查看后續(xù)內(nèi)容)

  • Element:存放上下文,通過(guò) Element 遍歷視圖樹,Element 同時(shí)持有 Widget 和 RenderObject

  • RenderObject:根據(jù) Widget 的布局屬性進(jìn)行 layout,paint Widget 傳人的內(nèi)容

  更新樹

  ★為什么 widget 都是 immutable?

  Flutter 界面開(kāi)發(fā)是一種響應(yīng)式編程,主張 simple is fast,F(xiàn)lutter 設(shè)計(jì)的初衷希望數(shù)據(jù)變更時(shí)發(fā)送通知到對(duì)應(yīng)的可變更節(jié)點(diǎn)(可能是一個(gè) StatefullWidget 子節(jié)點(diǎn),也可以是 rootWidget),由上到下重新 create widget 樹進(jìn)行刷新,這種思路比較簡(jiǎn)單,不用關(guān)心數(shù)據(jù)變更會(huì)影響到哪些節(jié)點(diǎn)。

  ★widget 重新創(chuàng)建,element 樹和 renderObject 樹是否也重新創(chuàng)建?

  widget 只是一個(gè)配置數(shù)據(jù)結(jié)構(gòu),創(chuàng)建是非常輕量的,加上 Flutter 團(tuán)隊(duì)對(duì) widget 的創(chuàng)建/銷毀做了優(yōu)化,不用擔(dān)心整個(gè) widget 樹重新創(chuàng)建所帶來(lái)的性能問(wèn)題,但是 renderobject 就不一樣了,renderobject 涉及到 layout、paint 等復(fù)雜操作,是一個(gè)真正渲染的 view,整個(gè) view 樹重新創(chuàng)建開(kāi)銷就比較大,所以答案是否定的。   

  ★樹的更新規(guī)則

  1. 找到 widget 對(duì)應(yīng)的 element 節(jié)點(diǎn),設(shè)置 element 為 dirty,觸發(fā) drawframe, drawframe 會(huì)調(diào)用 element 的 performRebuild ()進(jìn)行樹重建

  2. widget.build () == null, deactive element.child,刪除子樹,流程結(jié)束

  3. element.child.widget == NULL, mount 的新子樹,流程結(jié)束

  4. element.child.widget == widget.build () 無(wú)需重建,否則進(jìn)入流程5

  5. Widget.canUpdate (element.child.widget, newWidget) == true,更新 child 的 slot,element.child.update (newWidget)(如果 child 還有子節(jié)點(diǎn),則遞歸上面的流程進(jìn)行子樹更新),流程結(jié)束,否則轉(zhuǎn)6

  6. Widget.canUpdate (element.child.widget, newWidget) != true(widget 的 classtype 或者 key 不相等),deactivew element.child,mount 新子樹

  注意事項(xiàng):

  1. element.child.widget == widget.build (),不會(huì)觸發(fā)子樹的 update,當(dāng)觸發(fā) update 的時(shí)候,如果沒(méi)有生效,要注意 widget 是否使用舊 widget,沒(méi)有 new widget,導(dǎo)致 update 流程走到該 widget 就停止了。

  2. 子樹的深度變化,會(huì)引起子樹重建,如果子樹是一個(gè)復(fù)雜度很高的樹,可以使用 GlobalKey 做為子樹 widget 的 key。GlobalKey 具有緩存功能。

  ★如何觸發(fā)樹更新

  1. 全局更新:調(diào)用 runApp (rootWidget),一般 flutter 啟動(dòng)時(shí)調(diào)用后不再會(huì)調(diào)用。

  2. 局部子樹更新, 將該子樹做 StatefullWidget 的一個(gè)子 widget,并創(chuàng)建對(duì)應(yīng)的 State 類實(shí)例,通過(guò)調(diào)用 state.setState () 觸發(fā)該子樹的刷新。

  Widget

  StatefullWidget vs StatelessWidget

  1. StatelessWidget:無(wú)中間狀態(tài)變化的 widget,需要更新展示內(nèi)容就得通過(guò)重新 new,F(xiàn)lutter 推薦盡量使用 StatelessWidget。

  2. StatefullWidget:存在中間狀態(tài)變化,那么問(wèn)題來(lái)了,widget 不是都 immutable 的,狀態(tài)變化存儲(chǔ)在哪里?Flutter 引入 state 的類用于存放中間態(tài),通過(guò)調(diào)用 state.setState ()進(jìn)行此節(jié)點(diǎn)及以下的整個(gè)子樹更新。

  State 生命周期  

  1. initState (): state create 之后被 insert 到 tree 時(shí)調(diào)用的

  2. didUpdateWidget (newWidget):祖先節(jié)點(diǎn) rebuild widget 時(shí)調(diào)用

  3. deactivate ():widget 被 remove 的時(shí)候調(diào)用,一個(gè) widget 從 tree 中 remove 掉,可以在 dispose 接口被調(diào)用前,重新 instert 到一個(gè)新 tree 中

  4. didChangeDependencies ():

  5. 初始化時(shí),在 initState ()之后立刻調(diào)用

  6. 當(dāng)依賴的 InheritedWidget rebuild,會(huì)觸發(fā)此接口被調(diào)用

  7. build ():

  8. After calling [initState].

  9. After calling [didUpdateWidget].

  10. After receiving a call to [setState].

  11. After a dependency of this [State] object changes (e.g., an[InheritedWidget] referenced by the previous [build] changes).

  12. After calling [deactivate] and then reinserting the [State] object into the tree at another location.

  13. dispose ():Widget 徹底銷毀時(shí)調(diào)用

  14. reassemble (): hot reload 調(diào)用

  注意事項(xiàng):

  1. A頁(yè)面 push 一個(gè)新的頁(yè)面B,A頁(yè)面的 widget 樹中的所有 state 會(huì)依次調(diào)用 deactivate (), didUpdateWidget (newWidget)、build ()(這里懷疑是 bug,A頁(yè)面 push 一個(gè)新頁(yè)面,理論上并沒(méi)有將A頁(yè)面進(jìn)行 remove 操作),當(dāng)然從功能上,沒(méi)有看出來(lái)有什么異常。

  2. 當(dāng) ListView 中的 item 滾動(dòng)出可顯示區(qū)域的時(shí)候,item 會(huì)被從樹中 remove 掉,此 item 子樹中所有的 state 都會(huì)被 dispose,state 記錄的數(shù)據(jù)都會(huì)銷毀,item 滾動(dòng)回可顯示區(qū)域時(shí),會(huì)重新創(chuàng)建全新的 state、element、renderobject。

  3. 使用 hot reload 功能時(shí),要特別注意 state 實(shí)例是沒(méi)有重新創(chuàng)建的,如果該 state 中存在一下復(fù)雜的資源更新需要重新加載才能生效,那么需要在 reassemble ()添加處理,不然當(dāng)你使用 hot reload 時(shí)候可能會(huì)出現(xiàn)一些意想不到的結(jié)果,例如,要將顯示本地文件的內(nèi)容到屏幕上,當(dāng)你開(kāi)發(fā)過(guò)程中,替換了文件中的內(nèi)容,但是 hot reload 沒(méi)有觸發(fā)重新讀取文件內(nèi)容,頁(yè)面顯示還是原來(lái)的舊內(nèi)容。

  數(shù)據(jù)流轉(zhuǎn)

  ★從上往下

  數(shù)據(jù)從根往下傳數(shù)據(jù),常規(guī)做法是一層層往下,當(dāng)深度變大,數(shù)據(jù)的傳輸變的困難,F(xiàn)lutter 提供 InheritedWidget 用于子節(jié)點(diǎn)向祖先節(jié)點(diǎn)獲取數(shù)據(jù)的機(jī)制,如下例子:

  child 及其以下的節(jié)點(diǎn)可以通過(guò)調(diào)用下面的接口讀取 color 數(shù)據(jù):

  說(shuō)明:BuildContext 就是 Element 的一個(gè)接口類

  context.inheritFromWidgetOfExactType (FrogColor)其實(shí)是通過(guò) context/element 往上遍歷樹,查找到***個(gè) FrogColor 的祖先節(jié)點(diǎn),取該節(jié)點(diǎn)的 widget 對(duì)象。

  ★從下往上

  子節(jié)點(diǎn)狀態(tài)變更,向上上報(bào)通過(guò)發(fā)送通知的方式

  • 定義通知類,繼承至 Notification

  • 父節(jié)點(diǎn)使用 NotificationListener 進(jìn)行監(jiān)聽(tīng)捕獲通知

  • 子節(jié)點(diǎn)有數(shù)據(jù)變更調(diào)用下面接口進(jìn)行數(shù)據(jù)上報(bào)

  ★閑魚 Flutter 的界面框架設(shè)計(jì)

  

   

  Layout   

  ★Size 計(jì)算

  parent 傳入約束條件,在 dramframe 的 layout 階段,child 根據(jù)自身的渲染內(nèi)容返回 size。

  問(wèn)題:在 build ()階段獲取不到 size,很多時(shí)候需要提前知道部分 widget size 來(lái)進(jìn)行布局,解決方案當(dāng) widget 在對(duì)應(yīng) renderobject 的 layout 階段之后,發(fā)送一個(gè) LayoutChangeNotification,參考 SizeChangedLayoutNotifier class,但是 SizeChangedLayoutNotifier 沒(méi)有上報(bào) init layout size,可以自己參考這個(gè)實(shí)現(xiàn)封裝一個(gè) Notifier。

  ★Offset 計(jì)算

  1. renderObject 拿到計(jì)算好的 size,再加上一些布局屬性(align、paddig)等,計(jì)算 child 相對(duì) parent 的 offset。

  2. offset 存放在每個(gè) child renderObject 的 BoxParentData 中。

  3. 當(dāng) parent 擁有 mutil children 時(shí),BoxParentData 還用來(lái)存 children 兄弟節(jié)點(diǎn)之間的遍歷順序。   

  ★Relayout boundary

  renderObject 在 layout 階段做了 Relayout boundary 的優(yōu)化,當(dāng)子樹進(jìn)行 relayout 時(shí),滿足下面三種中的一種:

  • parentUsesSize == false

  • sizedByParent == true

  • constraints.isTight

  那么該 renderObject 設(shè)置為 Relayout boundary,也就是該 renderObject 的重新 layout 不觸發(fā) parent 的 layout,一般情況下開(kāi)發(fā)人員不需要關(guān)心 Relayout boundary,除非是使用 CustomMultiChildLayout。

  Paint

  ★L(fēng)ayer

  iOS 的每一個(gè) UIView 都有一個(gè) layer,F(xiàn)lutter 的 render object 不一定存在 layer,一般情況下一個(gè) renderObject 子樹都渲染在一個(gè) layer 上,那么什么 renderObject 具有 layer,子 renderObject 怎么渲染到這個(gè) layer?

  1. 當(dāng)一個(gè) renderObject 的
或者


,renderOject 會(huì)有對(duì)應(yīng)的 compositing layer。

  2. 子 renderObject 會(huì)對(duì)目標(biāo) layer 返回對(duì)應(yīng)的 offsetLayer, 目標(biāo) compositing layer 再根據(jù) offset 合成一個(gè)渲染的紋理 buffer。

  ★Repaint Boundary

  類似 Relayout boundary,Paint 階段也有 Repaint Boundary,目的和 layout 一樣,就是對(duì)應(yīng)子樹的 paint 不會(huì)導(dǎo)致外部的 repaint,但是 Relayout boundary 需要開(kāi)發(fā)人員自己設(shè)置,使用 RepaintBoundary widget 進(jìn)行設(shè)置,ListView 在渲染的 item 默認(rèn)都是使用了 RepaintBoundary,顯而易見(jiàn) ListView 的 children 之間都是相互獨(dú)立的。Flutter 建議復(fù)雜的 image 渲染使用 RepaintBoundary,image 的渲染需要 io 操作,然后解碼,***渲染,使用 RepaintBoundary 可以進(jìn)行 gpu 的緩存,但是不一定就會(huì)緩存,engine 會(huì)判斷這個(gè) image 是否足夠復(fù)雜,畢竟 gpu 緩存還是非常珍貴的,同時(shí) RepaintBoundary 還會(huì)對(duì)一些反復(fù)渲染的 layer 進(jìn)行緩存處理(反復(fù)渲染 3 次及以上,這個(gè)是 Flutter 的視頻中提到的)。

  結(jié)語(yǔ)

  Flutter 還處于 Beta 階段,有些界面編程的接口設(shè)計(jì)還不夠成熟,相比 iOS 和安卓生態(tài)還很不成熟,需要我們共同的創(chuàng)建,F(xiàn)lutter 提供的調(diào)試工具相比一開(kāi)始接觸的時(shí)候,已經(jīng)完善很多,讓我們給 Flutter 更多的耐心和包容,期待 Flutter 越來(lái)越完善。

  參考資料

責(zé)任編輯:張燕妮 來(lái)源: 阿里技術(shù)
相關(guān)推薦

2021-01-27 11:10:49

JVM性能調(diào)優(yōu)

2019-07-09 08:29:51

TCPIP協(xié)議

2019-11-28 09:33:08

Redis架構(gòu)互聯(lián)網(wǎng)

2018-04-25 10:13:30

Redis內(nèi)存模型

2019-11-20 10:07:07

Redis數(shù)據(jù)系統(tǒng)

2022-02-28 10:30:03

架構(gòu)代碼Native

2023-03-31 08:16:53

Flutter優(yōu)化內(nèi)存管理

2020-09-18 06:42:14

正則表達(dá)式程序

2024-04-30 11:11:33

aiohttp模塊編程

2020-08-27 07:34:50

Zookeeper數(shù)據(jù)結(jié)構(gòu)

2015-10-21 10:42:33

技術(shù)周刊

2023-12-08 17:59:55

工具Git LFS管理

2010-07-13 09:36:25

2010-11-19 16:22:14

Oracle事務(wù)

2020-09-21 09:53:04

FlexCSS開(kāi)發(fā)

2022-08-26 13:48:40

EPUBLinux

2009-08-25 16:27:10

Mscomm控件

2010-06-23 20:31:54

2023-04-26 15:43:24

容器編排容器編排工具

2023-11-20 08:18:49

Netty服務(wù)器
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)

主站蜘蛛池模板: 91美女在线| 国产精品一区二区久久 | 国产乱码精品一区二区三区忘忧草 | 成人黄色电影在线观看 | 亚洲免费一区二区 | 亚洲精品乱码久久久久久按摩观 | 欧美日韩一区二区视频在线观看 | 欧美日韩在线一区二区三区 | 欧美日韩在线一区 | 久久久久国产一区二区三区 | 精品视频一区二区三区在线观看 | 日韩成人影院在线观看 | av中文在线| 亚洲成人一区 | 盗摄精品av一区二区三区 | 日韩av三区 | 亚洲欧美国产精品久久 | 国产精品国产三级国产aⅴ原创 | 国产91亚洲精品 | 国产成人叼嘿视频在线观看 | 免费一级毛片 | 欧美精品一区二区免费 | h片在线看 | 日韩在线免费 | 免费在线观看av网站 | 日韩在线免费电影 | 国产在线不卡视频 | 国产高清在线观看 | 国产特一级黄色片 | 国产精品av久久久久久久久久 | 亚洲国产片 | 国产男人的天堂 | 视频在线亚洲 | 亚洲成人av| 91资源在线| 一级看片免费视频囗交动图 | 日韩成人av在线 | 国产精品久久久99 | 中文字幕啪啪 | 久久久高清 | 黄色播放|