[閒魚技術] Release Flutter的最後一千米

做者:閒魚技術-凱航android

Flutter是一個使用Dart語言開發的跨平臺移動UI框架,經過自建繪製引擎,能高性能、高保真地進行Android和IOS開發。在業界還未出現過Base Flutter的大型商業應用實戰驗證的狀況下,閒魚技術團隊在最複雜且重要的商品詳情頁做了相關的技術實踐並取得良好的結果。現嘗試經過本文向有興趣進行相似實踐的開發者或團隊分享過程當中的思考/實踐過程。ios

Flutter特點

面對一系列移動開發技術:IOS、Android、Weex,RN, Kotlin,H5... Flutter究竟特點何在? git

image.png

開發語言選擇

瞭解過Flutter的都知道,它採用Dart語言進行開發,而並不是Java,Javascript這類熱門語言,這是Flutter團隊對當前熱門的10多種語言慎重評估後的選擇。由於Dart囊括了多數編程語言的優勢,它更符合Flutter構建界面的方式。 github

image.png
Dart更多優點可查看 爲何Flutter會選擇Dart

Flutter vs ReactNative框架對比

ReactNative Flutter
image.png
image.png

ReactNative算法

  • 採用Javascript開發,需學React,成本高
  • 須要JavaScript橋接器,實現JS到Native轉化,性能耗損
  • 訪問原生UI,頻繁操做易出性能問題
  • 支持線上動態性,可有效避免頻繁更新版本

Fluttershell

  • 採用Dart開發,可直接編譯成Native代碼(易學)
  • 自帶UI組件和渲染器,僅依賴系統提供的Canvas(無橋接耗損)
  • 暫不支持線上動態性

Flutter更多特點能夠連接爲何說Flutter是革命性的?編程

每一個框架都是爲解決特定問題而產生的,不存在最好的框架,只有最適合你團隊的框架。閒魚是個業務快速發展的App,爲更多業務嘗試和探索,它採用現有流行的框架,能支持線上動態化需求。但出於個性化交互以及流暢性體驗(首頁、商品詳情、發佈閒置等),主鏈路依舊只採用原生開發。爲兼顧跨端開發及高性能需求,閒魚通過充分調用,最終選擇了Flutter。爲驗證Flutter的性能,閒魚挑選重要且複雜的主鏈路業務(商品詳情)做爲首個Flutter頁面實踐點,以這種方式來快速暴露並解決Flutter相關問題。緩存

閒魚Flutter突破點

Flutter與Native混合編程方案

隨着Flutter版本的不斷迭代,穩定性和質量逐漸完善,市場上純Flutter開發的App也不斷涌現。閒魚對Flutter採起「由點到面,逐一替換」 的策略,先將商品詳情遷移到Flutter頁面,後續逐步擴展到其餘功能模塊,但這樣就不可避免涉及到Flutter與Native頁面混合調用的場景(以下圖): 性能優化

image.png
對純Flutter工程而言,它主要經過FlutterView中Navigator來管理頁面間的跳轉;對純Native工程而言(如:android), 它主要經過系統中ActivityStackSupervisor類對頁面切換進行管理,這樣當Flutter與Native混合時,就面臨瀏覽一組頁面,兩套頁面管理方式(Flutter管理Flutter頁面,Native管理Native頁面), 若執行回退操做時,很難保證能回退到指望頁面。另外,Flutter工程中界面都是一個繼承自SurfaceView的FlutterView(說白了Flutter界面就一個View,不是Activity也不是Fragment),Flutter和Native組件間相互調用也不可避免。

Flutter Native(Android)
image.png
image.png

所以要混合調用就會涉及兩個問題:併發

  • 混合棧管理
  • 組件間調用

混合棧管理

Flutter出現的目的旨在統一Android/IOS兩端編程,所以徹底基於Flutter開發的App,只需提供一個包含FlutterView的頁面,後續頁面增長/刪除/跳轉均在FlutterView的Navigator中進行管理。但如今閒魚只是將部分模塊修改爲Flutter開發,咱們不可能爲統一頁面棧管理而將其餘全部頁面用Flutter重作一次,權衡成本與風險,亟需統一管理Native頁面和Flutter頁面跳轉交互的混合棧。爲此,閒魚提出了4種解決方案(以下圖):

image.png
因爲IOS有對外系統接口能夠方便管理頁面棧,所以主動記錄頁面棧信息就能夠解決混合棧管理(方案1),但Android任務棧由系統管理,且融合複雜的Activity回收機制,爲下降android學習成本,google並無對外提供頁面棧管理API,方案1方式行不通。爲統一android/IOS混合棧管理方式,從FlutterView上着手更爲可靠,以此爲引,閒魚提出兩種方式:

  1. 每啓動一個Activity就啓動一個新的FlutterView(方案4);
  2. 抽取單一FlutterView或FlutterNativeView,後續每啓動一個Activity都對FlutterView或FlutterNativeView進行復用(方案2或方案3);

考慮到每啓動一個頁面都新建立一套新的Flutter渲染機制,開銷太重,目前閒魚Flutter實踐採用方案2,相比而言,該方案性能相對穩定且易操做,下面就是否複用FlutterView進行對比,主要觀測Java內存和Native內存增長狀況:

數據代表:不復用FlutterView時平均打開一個頁面(空頁面),Java內存增加0.02M,Native內存增加0.73M;複用FlutterView時平均打開一個頁面(空頁面),Java內存增加0.019M,Native內存增加0.65M,所以,複用FlutterView在內存使用上是有優點的,如需更深瞭解可查看 Android Flutter內存初探。此外,相關方案的詳細表述在 How to manage page stack in flutter/native hybrid App 以及 Support multiple shells in a single process均有闡述。

組件間調用

image.png
組件間採用比較常見場景就是黑屏問題,出現該問題多數爲Layer衝突。從上圖(右)可知UI渲染原理:GPU的VSync信號同步到UI線程,UI線程使用Dart構建抽象的視圖結構(Layer Tree),接着在GPU線程進行圖層合成,且視圖數據提供給Skia引擎進行渲染生成GPU數據,最終經過OpenGL或Vulkan提供給GPU,由此能夠看出Flutter並不關心顯示器、視頻控制器以及GPU具體工做細節,它只關心發出的VSync信號,以求儘量快地在兩個VSync信號之間計算併合成視圖數據並提供給GPU。Flutter開發者都知道Flutter界面渲染時,使用的是FlutterViewController.view的Layer,假若Flutter頁面跳轉到Native作界面渲染相關邏輯時, Native也使用同一個Layer,這將會致使Flutter在release模式沒法渲染,LayerTree合成失敗即Layer衝突。不過這問題解決也很簡單,只須要採用Window或獨立View方式喚起Native便可。

解決了Flutter與Native混合編程所面臨的問題後,接下來要處理的就是混編工程問題,出現該問題的緣由仍是咱們的項目不是徹底的Flutter工程(即:android /ios + Flutter)所致。混合工程項目結構以及Flutter產物以下圖:

項目結構

image.png

Flutter產物

image.png

其實對通常Flutter工程而言,採用AndroidStudio編譯Flutter與編譯Native工程方式同樣,當將其部署到server端採用mtl編譯時,server缺乏Flutter編譯環境,於是致使Flutter工程沒法編譯。解決此問題能夠採起兩種方式:

  1. 在每一個server端部署Flutter編譯環境
  2. Native工程遠程依賴Flutter編譯產物

對1,對各server端都去部署Flutter環境有點不切實際(若server就那麼幾臺也能夠);對2,閒魚的作法是將Flutter工程編譯出的中間產物以AAR形式導出並上傳至maven庫,最後Native工程以依賴包形式將AAR打入最終apk中,這樣處理後解耦了Native團隊對Flutter團隊的依賴。固然,具體實踐過程當中確定沒有這麼簡單,咱們在編譯過程當中對Pod/Gradle編譯腳本、engine以及flutter_tools等均有所優化,對後續集團推廣Flutter奠基了基礎。

阿里Flutter生態適配

將Flutter應用於閒魚,不可避免須要使用集團提供的基礎組件庫,但這些組件庫都是Native,考慮到爲後續Flutter在集團推廣,打造阿里Flutter生態圈,閒魚團隊對集團內部基礎組件庫作了適配支撐,後續可創建私有倉庫,直接Git引用。

生態適配原理及性能

image.png
上圖(左)概述了Flutter平臺通道,使用MethodChannel在Client(UI)和主機(平臺)之間傳遞消息,消息和響應異步傳遞以確保用戶界面保持正常響應。對UI,Flutter的MethodChannel類能夠發送與方法調用相對應的消息;對平臺,Android端MethodChannel類和IOS端FlutterMethodChannel類能夠接收方法調用併發送結果,同時方法調用還能夠逆向發送,即以平臺做爲實現Dart方法的Client。值得注意的是Flutter Plugin開發相關原理也是如此。上圖(右)還對MethodChannel吞吐量性能進行了簡略表述。

多媒體解決方案

在之內容爲王的時代,多媒體技術備受關注,性能的好壞直接影響用戶體驗,但Flutter多媒體默認功能存在如下缺陷:

  • 功能單一,如:播放器缺乏濾鏡,與Native淘寶播放器存在必定差距
  • 兼容性欠佳

爲改善體驗,優化性能,閒魚對Flutter播放器以及圖片性能做出以下改進:

Texture對接自定義視頻播放器

image.png

具體方案:

image.png

圖片性能優化

有過移動開發經驗的都知道,圖片展現是OOM出現的高頻場景,而Flutter默認採用基於LRU算法的圖片緩存策略,且圖片緩存MaxSize=1000,佔用內存較高,爲此閒魚採用如下兩種策略對圖片性能進行優化

image.png

Flutter 上線效果 & 性能對比 & 成熟度

解決了Flutter存在的問題,要的就是產品可以以一種性能穩定、交互流暢、界面美觀的姿態呈如今用戶面前,下面就以閒魚寶貝詳情線上Flutter版本爲引,對Flutter應用頁面展現、性能以及成熟度進行闡述。

閒魚寶貝詳情Flutter應用線上效果

image.png

Native性能對比

測試環境

  • 測試機型:iphone6
  • IOS版本:11.3
  • 閒魚版本: 6.1.3
  • 測試方法: 在Flutter版本和Native版本各自寶貝詳情頁面執行相同操做:即從我發佈的頁面進行10個不一樣詳情頁面

注: 下圖僅對Flutter和Native性能進行了粗略對比

image.png

Flutter成熟度

image.png
上圖爲crash收斂曲線圖,可容易看出通過幾個版本迭代,crash率基本趨於穩定,體感與Native能夠媲美。

延展討論

目前Flutter尚處於Preview階段,沒有通過大規模實踐驗證,框架成熟度及穩定性仍有待完善。上文僅僅是將閒魚團隊在實踐Flutter開發時碰到部分問題及解決方案進行了簡略闡述,一個產品從開發到上線所面臨的問題,確定遠不及這些。隨着Flutter覆蓋場景的增長,難題也會不斷涌現,健全有效的性能及穩定性監控體系不可或缺。爲建設基於Flutter全新的一體化研發體系,提升開發效率,對動態化需求、規範Dart編碼、統一中間件橋接機制、快速發版能力及完備的自動化測試建設等一系列問題亟需解決,假若您對此感興趣,歡迎一塊兒交流學習~

簡歷投遞:guicai.gyx@alibaba-inc.com

相關文章
相關標籤/搜索