日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關咨詢
選擇下列產品馬上在線溝通
服務時間:8:30-17:00
你可能遇到了下面的問題
關閉右側工具欄

新聞中心

這里有您想知道的互聯(lián)網營銷解決方案
移動開發(fā)新利器|一文深入了解Flutter界面開發(fā)

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

為都江堰等地區(qū)用戶提供了全套網頁設計制作服務,及都江堰網站建設行業(yè)解決方案。主營業(yè)務為成都網站制作、成都網站設計、外貿營銷網站建設、都江堰網站設計,以傳統(tǒng)方式定制建設網站,并提供域名空間備案等一條龍服務,秉承以專業(yè)、用心的態(tài)度為用戶提供真誠的服務。我們深信只要達到每一位用戶的要求,就會得到認可,從而選擇與我們長期合作。這樣,我們也可以走得更遠!

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

  Flutter 框架簡介

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

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

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

  Rendering Pipeline

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

  視圖樹

  Widget&Element&RenderObject

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

  創(chuàng)建樹

  1. 創(chuàng)建 widget 樹

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

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

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

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

  更新樹

  為什么 widget 都是 immutable?

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

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

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

  樹的更新規(guī)則

  1. 找到 widget 對應的 element 節(jié)點,設置 element 為 dirty,觸發(fā) 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 還有子節(jié)點,則遞歸上面的流程進行子樹更新),流程結束,否則轉6

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

  注意事項:

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

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

  如何觸發(fā)樹更新

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

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

  Widget

  StatefullWidget vs StatelessWidget

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

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

  State 生命周期  

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

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

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

  4. didChangeDependencies ():

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

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

  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 滾動出可顯示區(qū)域的時候,item 會被從樹中 remove 掉,此 item 子樹中所有的 state 都會被 dispose,state 記錄的數據都會銷毀,item 滾動回可顯示區(qū)域時,會重新創(chuàng)建全新的 state、element、renderobject。

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

  數據流轉

  從上往下

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

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

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

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

  從下往上

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

  • 定義通知類,繼承至 Notification

  • 父節(jié)點使用 NotificationListener 進行監(jiān)聽捕獲通知

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

  閑魚 Flutter 的界面框架設計

  

  

  Layout  

  Size 計算

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

  問題:在 build ()階段獲取不到 size,很多時候需要提前知道部分 widget size 來進行布局,解決方案當 widget 在對應 renderobject 的 layout 階段之后,發(fā)送一個 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 兄弟節(jié)點之間的遍歷順序?! ?/p>

  Relayout boundary

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

  • parentUsesSize == false

  • sizedByParent == true

  • constraints.isTight

  那么該 renderObject 設置為 Relayout boundary,也就是該 renderObject 的重新 layout 不觸發(fā) parent 的 layout,一般情況下開發(fā)人員不需要關心 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 需要開發(fā)人員自己設置,使用 RepaintBoundary widget 進行設置,ListView 在渲染的 item 默認都是使用了 RepaintBoundary,顯而易見 ListView 的 children 之間都是相互獨立的。Flutter 建議復雜的 image 渲染使用 RepaintBoundary,image 的渲染需要 io 操作,然后解碼,***渲染,使用 RepaintBoundary 可以進行 gpu 的緩存,但是不一定就會緩存,engine 會判斷這個 image 是否足夠復雜,畢竟 gpu 緩存還是非常珍貴的,同時 RepaintBoundary 還會對一些反復渲染的 layer 進行緩存處理(反復渲染 3 次及以上,這個是 Flutter 的視頻中提到的)。

  結語

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

  參考資料

  • https://github.com/flutter/flutter

  • https://github.com/flutter/engine

  • https://flutter.io/


新聞名稱:移動開發(fā)新利器|一文深入了解Flutter界面開發(fā)
文章地址:http://www.dlmjj.cn/article/djgeeie.html