ICBU建設Flutter的核心目標是保障Flutter的性能和質量,進一步提升提效效果,進一步擴大提效範圍。本次分享將由ICBU Flutter架構師路少德為大家詳細介紹ICBU在Flutter實踐中的思考和沉澱。整體分為兩部分,第一部分通過業務背景和技術原理推導出接入Flutter的必要性和待解決的問題。第二部分以接入工作中的混合工程和混合棧為重點進行技術上的闡述。
演講嘉賓簡介:路少德,花名白及,ICBU Flutter架構師,將Flutter接入ICBU,設計實現ICBU的Flutter架構和基礎組件,實現和輸出ICBU的混合棧、多語言等能力,並推動ICBU無線技術部持續朝Flutter化演進。
以下內容根據演講視頻以及PPT整理而成。
觀看回放http://mudu.tv/watch/5817421
本次分享主要圍繞以下四個方面:
一、背景介紹 二、Flutter技術接入 三、Flutter能力補齊 四、結果&規劃
一、背景介紹
ICBU Flutter現狀
首先在場景覆蓋層面,ICBU目前有30+Flutter頁面,交易業務覆蓋90%,目前所有新頁面都選擇Flutter進行開發。在人員層面,所有無線開發掌握Flutter開發知識,Android&iOS間的壁壘正在逐漸消失。在提效效果層面,目前已累計提效大量人日,同時獲得了不錯的技術成果,如參與AliFlutter的建設,並提出了基於官方的工程體系和混合棧方案,及多語言、本地化等多種國際化方案。
下圖是APP使用Flutter後的頁面,包括Android版的會話列表、iOS的會話列表、RTL(阿拉伯語)的會話列表,以及購物車等交互非常複雜的頁面。
為什麼選擇Flutter
Alibaba.com屬於B類國際電商業務,B類國際電商業務在當下的特點是發展非常迅速,業務壓力很大,每次的業務需求都會超出人員需求,並且開發資源會被其他事情佔用(如技術架構演化),此時不得不進行非關鍵業務的取捨或者加班加點趕工期。但這兩種策略都不能從根本上解決問題,ICBU希望的是通過提高研發效率,用更少的開發資源承擔更多的業務需求,甚至可以解放生產力做其他事情。第二個特點是業務非常複雜,下圖右側展示瞭如果需要進行跨境B類貿易,則需要經過的28步流程。在如此複雜的業務場景下,由於Android和iOS開發對業務的理解的不同,導致業務實現出現差異,從而不得不耗費更多的開發資源進行對齊,所以ICBU希望有跨端一致性的框架解決這個問題。
再進一步而言,ICBU無線技術訴求是有一個取代Native開發的跨端框架。對於Native大家已經非常熟悉,它的特點包括極致體驗,優秀性能,同時具備高穩定性。在此之前,ICBU已經嘗試過很多跨端框架,包括H5、小程序、uni-app、Weex、以及阿里集團內部的DinamicX等動態組件,上述框架都有各自的優點和使用場景,但是各自都存在一些問題,如性能差、一致性差、存在能力缺陷、開發效率低等,都無法取代Native開發。
在18年左右,Flutter進入了大家的視線中,下圖右側展示了Weex、Native與Flutter技術原理的對比,以Android開發為例,Native佈局的渲染就是將Layout XML變成了Native View Tree,Weex也是藉助了類似的原理,即上層使用JS,在渲染時經過幾次DOM的轉換,最終以Native View Tree進行頁面的渲染。因為Weex相比與Native多一層JS,所以性能上無法和Native一樣,並且隨著頁面複雜度越來越高,與Native的性能差距也會越來越大。而Flutter運行在Dart虛擬機上,直接進行繪製,沒有藉助Native View Tree。通過技術原理,我們可以推斷Flutter具備完美一致性,另外就是性能上限比較高。
二、Flutter技術接入
既然決定採用Flutter,就需要弄清楚我們要解決什麼問題,以及解決的方案。下圖展示了Flutter與Android的比對,並且劃分了四個技術體系,分別包括技術環境,工程體系,能力&效率,以及穩定性。而且分別列出來Android和Flutter當時的狀態,通過一一對比,就可以發現每一項問題及對應的解決方案,再將解決方案重新規劃。如首先解決接入問題,即混合工程和混合棧問題;再針對Flutter薄弱點進行能力補齊,即視頻播放和數據預加載能力;接下來是解決Flutter動態化能力;最後在前面的解決方案都順利的情況下,利用Flutter跨端提效的特點,在更多應用上進行拓盤,如應用於賣家App或者PC上。
Flutter-混合工程
Flutter開發大體分為兩種,一種是原生開發,另一種是混合開發。原生開發適合開發新App,混合開發更適合存量App,有基建成本,但官方支持不夠成熟。混合開發核心需求解決兩個問題,即混合工程和混合棧。混合技術首先需要解決前三個問題:
- Native工程如何源碼依賴Flutter工程;
- Flutter工程如何生成中間產物(如Android上是aar,iOS上是Framework等);
- Native工程如何依賴中間產物。
右側是以AliFlutter為代表的工程結構,下層是AliFlutter基建,原則是將AliFlutter的基建都以產物的形式交付給業務團隊。業務團隊在上層只需要關心三個工程:Native主工程、Flutter主工程、Flutter業務工程。
打包
混合工程第二個問題是打包。下圖左側是在源碼依賴情況下,工程結構的示意圖。右側是在產物依賴情況下,工程結構示意圖。黃色部分是Native代碼實現,藍色部分是Flutter代碼實現。從左至右,Native部分代碼沒有很大變化,而Flutter代碼發生了翻天覆地的變化。所有Flutter代碼都被打包一個產物,而Flutter插件所使用的Android代碼或iOS代碼被分門別類的打成獨立的包。如果再細分,可以變成三個問題,首先是如何區分release和debug產物;其次是如何遞歸的打出插件的Native產物(圖中黃色部分);最後是如何一鍵依賴如此多的產物。通過Shell腳本打出release和debug產物,再通過Gradle腳本打包Flutter產物,再通過Gradle腳本注入的方式將每個插件的信息注入到每個插件的打包腳本中,如此便可以打出每個產物,且每個產物都配有POM文件,通過POM文件可以實現一鍵遞歸依賴。
Flutter-混合棧
混合棧分為兩個問題,首先是引擎複用方案,其次是Native頁面及Flutter頁面的約束方案。下圖左側是不復用引擎和複用引擎的區別。複用引擎無需在每個頁面生成獨立的引擎,從而減少引擎內存的佔用,減少引擎創建時間,提高內存穩定性,並且減少頁面啟動時間。
複用引擎需要解決的核心問題是生命週期的處理,也就是Native生命週期、Flutter引擎的綁定和解綁、及Flutter自己的生命週期這三部分之間的關係和處理非常關鍵。
最簡單的例子,當頁面從打開到關閉,是一個線性的流程,故可以與Flutter的生命週期進行綁定。第二點,從A頁面打開半透明B頁面時,Android不會調用A的onStop方法,如果在onStop方法上做一些特殊處理,則不會被調用,出現問題。第三點,如果從B頁面返回A頁面時,會先調B的onPause方法、再調A的onStart、onResume、再調B的onStop、onDetach,此時如果在onResume中做一些正向操作,同時在onDetach做負向操作,就會出現問題。最後ViewPager和Transaction的Fragment生命週期管理問題非常令人頭疼,因為ViewPager和Transaction對生命週期的處理都不同,而且會調用自己獨特的方法,對生命週期的處理進一步增加難度。
綜上,Android的生命週期十分複雜,牽一髮動全身,同時Flutter引擎處理也十分複雜,如果綁定和解綁時序錯亂會帶來非常嚴重的後果。
引擎複用-技術方案
下圖梳理了將Flutter引擎與Android生命週期間的綁定和解綁的原理,如引擎綁定和解綁分別放在onResume和onPause上,從而避免半透明界面問題,同時又將Flutter生命週期也放在其中。針對返回頁面問題,加上了延時和輪詢隊列保證onResume中的方法晚於上一個頁面銷燬的方法,突破生命週期的限制。
下圖通過代碼對比了三種不同的方案,第一個是官方的Flutter SDK,第二個是閒魚的Flutter Boost,最後是ICBU混合棧。可以發現官方的Flutter SDK直接調用了onStart方法,並沒有解決半透明頁面問題。同時在B頁面返回A頁面時,使用了Handler().post()的消息隊列,造成一定程度上的時延。Flutter Boost對生命週期做了很多梳理,但是也做了一些簡化,如onPause時沒有調用Flutter的postPaused方法,這樣可以極大的簡化生命週期的複雜度,梳理出所需要的生命週期。而ICBU的混合棧則是兼顧了Native和Flutter的生命週期。
上層頁面約束
上層頁面約束最核心的是要保證一個Native頁面要一比一的對應Flutter頁面。ICBU的頁面約束是利用了Native的頁面棧及Flutter的頁面棧,通過兩個棧一起出一起進的行為保證一比一的頁面約束。但其實頁面約束層還是存在幾點問題,首先是穩定性低,引擎複用一旦出錯會導致約束出錯,其實是適用場景有限,不支持多Flutter Tab。與之對應,Flutter Boost的頁面約束層非常優秀,它通過對每一個Flutter容器單開一個Navigator,來保證頁面約束是一比一的。因此ICBU計劃接入Flutter Boost,共建Flutter Boost。
階段性總結
ICBU混合工程實現了一鍵切換源碼和產物依賴,實現了一鍵本地或MTL打包,搭建了AliFlutter初版工程框架。在混合棧方面,主要是在內存和頁面啟動時長方面的提升,複用引擎相比於不復用引擎在內存上有所減少,下圖中只是對比了兩個頁面,隨著打開頁面的增加,內存差距也會越來越大。同時複用引擎節省了大概300毫秒左右的啟動時長。在經歷了半年的初步建設之後,Flutter在提效,UI豐富力及極致體驗方面都表現優秀,當然在視頻播放、動態化能力、架構優化等方面還有待優化,進一步提升研發效率。
三、Flutter能力補齊
視頻播放解決方案
一般的視頻方案是直接提供View,然後直接播放。但在Flutter中很難複用View,即便可以,代價也非常昂貴。ICBU借鑑Flutter官方和閒魚的代碼,整個Flutter視頻渲染流程如下圖右側,我們通過紋理傳遞視頻實現跨技術棧的渲染,Native視頻播放器將自己的視頻流通過紋理渲染到Flutter的控件上。對視頻的播放暫停等控制行為通過Flutter自帶的消息通道進行傳遞。如此便可以封裝成一個完整的Flutter的播放組件,同時底層可以複用App中已有的視頻播放。
視頻技術方案如下圖,底層是播放器,通過紋理的傳遞向Flutter提供視頻播放的能力,播放控制和優化策略是通過統一視頻開放接口向上支持。Flutter自己可以在上層可以封裝各種組件,進行業務提效。Flutter視頻頁啟動時長相較於Weex降低了400ms,對應視頻方案已輸出給考拉,健康等用戶。
Flutter動態化
大家都知道Flutter的動態化是它的薄弱點,下圖展示了業界所有的動態化技術方案在各個方面的比對情況。首先一類是編譯產物下發,如Hot patch;第二類是JS類框架,如RN,Weex,H5;第三類是以淘係為代表的動態組件框架,如DinamicX。經過反覆的考量,編譯產物下發的政策風險較高,Google Play和AppStore都不允許此方案的上線,而JS類框架性能損失很大,而以DinamicX為代碼的動態組件方案在各個方面都取得了優秀的平衡。DinamicX是組件級接入,使用非常靈活,而且它使用了Android Layout Xml,學習成本更低。其配合Flutter可以達到1+1>2的效果,藉助Flutter的一致性,不需要再維護Android和iOS兩套SDK,維護成本有所減少,針對已使用DinamicX的業務,其遷移成本更低。
四、結果&規劃
ICBU的Flutter建設工作在前期參與了同閒魚的技術共建,實現了多種控件,具備了國際化RTL能力,適配了高版本的SDK。在未來,我們期望所有Native頁面將被Flutter頁面取代,針對動態化場景使用DinamixX,若還是有欠缺,則使用H5,Weex,小程序等進行補全。
架構圖
通過左側工程體系的建設,保證研發效率,同時針對不同的中間件進行建設,以支持不同的業務場景,在未來,除了Android和iOS,我們還希望Flutter可以帶來更多平臺上的優勢。
結果
從結果層面,我們沉澱了豐富的技術,包括混合工程體系,中間件等。同時通過與阿里巴巴集團的緊密合作,向AliFlutter提供了工程體系及多語言能力,同閒魚一起共建了Dx-Flutter。使用Flutter之後,開發效率相比於使用Native提升了60%以上。通過Flutter,使得資源彈性增大,避免出現Android和iOS資源不平衡導致的業務不一致的問題。最後,通過Flutter節省了大量人日,讓更多的人力投入到端智能和數據化及更多其他業務中。技術影響力也在逐漸擴大,目前很多的技術方案已提供阿里集團內部很多BU。
未來規劃
ICBU建設Flutter的核心目標是保障Flutter的性能和質量,進一步提升提效效果,進一步擴大提效範圍。拆分開來分別對應性能&質量,接入及拓盤。在早期,Flutter本身不夠成熟,各個問題的最優解都不是很清楚。在現在,Flutter逐漸成熟,大家對各部分的最優解都達成了一致,阿里希望有個統一的Flutter中臺,承擔大部分的基建工作。因此,ICBU也計劃進一步接入AliFlutter,減少維護成本,同時對AliFlutter形成反饋,增加Flutter基建的價值。
關注「淘系技術」微信公眾號,一個有溫度有內容的技術社區~