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

移動開發新利器 | 一文深入了解 Flutter 界面開發

新聞 Android
談到移動端開發,大家心中肯定會涌現出一系列名詞:iOS、Android、Weex,H5... 那為何還使用 Flutter?其實,Flutter 通過自建繪制引擎,具備與 Native 媲美的性能指數,且有很好的兩端一致性,因此 Flutter 提供了一種新的可選項。

 [[232114]]

  阿里妹導讀:談到移動端開發,大家心中肯定會涌現出一系列名詞:iOS、Android、Weex,H5... 那為何還使用 Flutter?其實,Flutter 通過自建繪制引擎,具備與 Native 媲美的性能指數,且有很好的兩端一致性,因此 Flutter 提供了一種新的可選項。閑魚寶貝詳情頁實踐上線也證明了這點,可以在性能無損前提下降低 iOS&Android 開發成本。

  本文由閑魚技術團隊出品。它將為你深入介紹 Flutter framework 關于視圖樹的創建與管理機制、布局、渲染的原理,以及 Flutter 布局與渲染相關性能優化的設計思路的文章。同時介紹在使用 Flutter 開發過程中,遇到的一些坑和相應的解決方案。

  Flutter 框架簡介

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

  2. 界面開發語言使用 dart,底層渲染引擎使用C, C++。

  3. 組合大于繼承,控件本身通常由許多小型、單用途的控件組成,結合起來產生強大的效果,類的層次結構是扁平的,以***化可能的組合數量。

  Rendering Pipeline

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

  視圖樹

  Widget&Element&RenderObject

  Flutter 視圖樹包含了三種樹,上圖只是介紹了三顆樹的基礎 class 的對應關系和功能介紹。

  創建樹

  1. 創建 widget 樹

  2. 調用 runApp (rootWidget),將 rootWidget 傳給 rootElement,做為 rootElement 的子節點,生成 Element 樹,由 Element 樹生成 Render 樹

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

  • Element:存放上下文,通過 Element 遍歷視圖樹,Element 同時持有 Widget 和 RenderObject

  • RenderObject:根據 Widget 的布局屬性進行 layout,paint Widget 傳人的內容

  更新樹

  ★為什么 widget 都是 immutable?

  Flutter 界面開發是一種響應式編程,主張 simple is fast,Flutter 設計的初衷希望數據變更時發送通知到對應的可變更節點(可能是一個 StatefullWidget 子節點,也可以是 rootWidget),由上到下重新 create widget 樹進行刷新,這種思路比較簡單,不用關心數據變更會影響到哪些節點。

  ★widget 重新創建,element 樹和 renderObject 樹是否也重新創建?

  widget 只是一個配置數據結構,創建是非常輕量的,加上 Flutter 團隊對 widget 的創建/銷毀做了優化,不用擔心整個 widget 樹重新創建所帶來的性能問題,但是 renderobject 就不一樣了,renderobject 涉及到 layout、paint 等復雜操作,是一個真正渲染的 view,整個 view 樹重新創建開銷就比較大,所以答案是否定的。   

  ★樹的更新規則

  1. 找到 widget 對應的 element 節點,設置 element 為 dirty,觸發 drawframe, drawframe 會調用 element 的 performRebuild ()進行樹重建

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

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

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

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

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

  注意事項:

  1. element.child.widget == widget.build (),不會觸發子樹的 update,當觸發 update 的時候,如果沒有生效,要注意 widget 是否使用舊 widget,沒有 new widget,導致 update 流程走到該 widget 就停止了。

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

  ★如何觸發樹更新

  1. 全局更新:調用 runApp (rootWidget),一般 flutter 啟動時調用后不再會調用。

  2. 局部子樹更新, 將該子樹做 StatefullWidget 的一個子 widget,并創建對應的 State 類實例,通過調用 state.setState () 觸發該子樹的刷新。

  Widget

  StatefullWidget vs StatelessWidget

  1. StatelessWidget:無中間狀態變化的 widget,需要更新展示內容就得通過重新 new,Flutter 推薦盡量使用 StatelessWidget。

  2. StatefullWidget:存在中間狀態變化,那么問題來了,widget 不是都 immutable 的,狀態變化存儲在哪里?Flutter 引入 state 的類用于存放中間態,通過調用 state.setState ()進行此節點及以下的整個子樹更新。

  State 生命周期  

  1. initState (): state create 之后被 insert 到 tree 時調用的

  2. didUpdateWidget (newWidget):祖先節點 rebuild widget 時調用

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

  4. didChangeDependencies ():

  5. 初始化時,在 initState ()之后立刻調用

  6. 當依賴的 InheritedWidget rebuild,會觸發此接口被調用

  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 徹底銷毀時調用

  14. reassemble (): hot reload 調用

  注意事項:

  1. A頁面 push 一個新的頁面B,A頁面的 widget 樹中的所有 state 會依次調用 deactivate (), didUpdateWidget (newWidget)、build ()(這里懷疑是 bug,A頁面 push 一個新頁面,理論上并沒有將A頁面進行 remove 操作),當然從功能上,沒有看出來有什么異常。

  2. 當 ListView 中的 item 滾動出可顯示區域的時候,item 會被從樹中 remove 掉,此 item 子樹中所有的 state 都會被 dispose,state 記錄的數據都會銷毀,item 滾動回可顯示區域時,會重新創建全新的 state、element、renderobject。

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

  數據流轉

  ★從上往下

  數據從根往下傳數據,常規做法是一層層往下,當深度變大,數據的傳輸變的困難,Flutter 提供 InheritedWidget 用于子節點向祖先節點獲取數據的機制,如下例子:

  child 及其以下的節點可以通過調用下面的接口讀取 color 數據:

  說明:BuildContext 就是 Element 的一個接口類

  context.inheritFromWidgetOfExactType (FrogColor)其實是通過 context/element 往上遍歷樹,查找到***個 FrogColor 的祖先節點,取該節點的 widget 對象。

  ★從下往上

  子節點狀態變更,向上上報通過發送通知的方式

  • 定義通知類,繼承至 Notification

  • 父節點使用 NotificationListener 進行監聽捕獲通知

  • 子節點有數據變更調用下面接口進行數據上報

  ★閑魚 Flutter 的界面框架設計

  

   

  Layout   

  ★Size 計算

  parent 傳入約束條件,在 dramframe 的 layout 階段,child 根據自身的渲染內容返回 size。

  問題:在 build ()階段獲取不到 size,很多時候需要提前知道部分 widget size 來進行布局,解決方案當 widget 在對應 renderobject 的 layout 階段之后,發送一個 LayoutChangeNotification,參考 SizeChangedLayoutNotifier class,但是 SizeChangedLayoutNotifier 沒有上報 init layout size,可以自己參考這個實現封裝一個 Notifier。

  ★Offset 計算

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

  2. offset 存放在每個 child renderObject 的 BoxParentData 中。

  3. 當 parent 擁有 mutil children 時,BoxParentData 還用來存 children 兄弟節點之間的遍歷順序。   

  ★Relayout boundary

  renderObject 在 layout 階段做了 Relayout boundary 的優化,當子樹進行 relayout 時,滿足下面三種中的一種:

  • parentUsesSize == false

  • sizedByParent == true

  • constraints.isTight

  那么該 renderObject 設置為 Relayout boundary,也就是該 renderObject 的重新 layout 不觸發 parent 的 layout,一般情況下開發人員不需要關心 Relayout boundary,除非是使用 CustomMultiChildLayout。

  Paint

  ★Layer

  iOS 的每一個 UIView 都有一個 layer,Flutter 的 render object 不一定存在 layer,一般情況下一個 renderObject 子樹都渲染在一個 layer 上,那么什么 renderObject 具有 layer,子 renderObject 怎么渲染到這個 layer?

  1. 當一個 renderObject 的
或者


,renderOject 會有對應的 compositing layer。

  2. 子 renderObject 會對目標 layer 返回對應的 offsetLayer, 目標 compositing layer 再根據 offset 合成一個渲染的紋理 buffer。

  ★Repaint Boundary

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

  結語

  Flutter 還處于 Beta 階段,有些界面編程的接口設計還不夠成熟,相比 iOS 和安卓生態還很不成熟,需要我們共同的創建,Flutter 提供的調試工具相比一開始接觸的時候,已經完善很多,讓我們給 Flutter 更多的耐心和包容,期待 Flutter 越來越完善。

  參考資料

責任編輯:張燕妮 來源: 阿里技術
相關推薦

2021-01-27 11:10:49

JVM性能調優

2019-07-09 08:29:51

TCPIP協議

2019-11-28 09:33:08

Redis架構互聯網

2018-04-25 10:13:30

Redis內存模型

2019-11-20 10:07:07

Redis數據系統

2022-02-28 10:30:03

架構代碼Native

2023-03-31 08:16:53

Flutter優化內存管理

2020-09-18 06:42:14

正則表達式程序

2024-04-30 11:11:33

aiohttp模塊編程

2015-10-21 10:42:33

技術周刊

2020-08-27 07:34:50

Zookeeper數據結構

2023-12-08 17:59:55

工具Git LFS管理

2010-06-23 20:31:54

2010-07-13 09:36:25

2010-11-19 16:22:14

Oracle事務

2009-08-25 16:27:10

Mscomm控件

2020-09-21 09:53:04

FlexCSS開發

2022-08-26 13:48:40

EPUBLinux

2020-07-20 06:35:55

BashLinux

2022-03-08 13:42:13

橫向移動網絡攻擊
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91精品午夜窝窝看片 | 日韩欧美国产精品综合嫩v 一区中文字幕 | www久久av| 国产视频91在线 | 日韩在线看片 | 亚洲理论在线观看电影 | 99精品在线观看 | 国产一区在线免费观看 | 欧美午夜精品 | 日韩一级免费电影 | 啪啪综合网 | 久久久国产视频 | 在线观看视频亚洲 | 精品一区av | 亚洲精品在线视频 | 亚洲免费高清 | 日本h片在线观看 | 久久久久国产精品一区二区 | 成年人在线| 草久久久 | 色天天综合 | 在线观看av不卡 | 亚洲精品免费观看 | 中文区中文字幕免费看 | 99久久久国产精品免费消防器 | 婷婷在线免费 | 欧美三级久久久 | 国产精品99久久久久久久久久久久 | 欧美日韩久久精品 | 噜噜噜色网 | 久久久久欧美 | 伊人伊人网 | 91福利影院| 欧美一级久久 | 一区二区三区在线 | 亚洲成人精品国产 | 日韩av免费在线观看 | 亚洲成人免费视频在线观看 | 91超碰在线观看 | av激情影院 | 国产精品久久久久久久久大全 |