天天看點

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

Flutter作為目前最火的跨平台研發方案,它到底好在哪裡?餓了麼從2018年下半年開始接觸Flutter,并在多個App大量落地Flutter業務。餓了麼對Flutter的期待是保質提效,賦能業務。阿裡巴巴新零售淘系技術AliFlutter系列第八場直播中邀請了蜂鳥大前端資深iOS工程師李永光為大家介紹餓了麼為了”保質提效,賦能業務”,選擇Flutter作為跨平台研發方案的緣由,Flutter在餓了麼應用與落地情況,餓了麼在Flutter應用過程中的基礎建設和沉澱。相信能給大家帶來更多嘗試使用Flutter、以及把Flutter實際用于業務開發的信心和決心。

演講嘉賓簡介:李永光,花名雍光,蜂鳥大前端資深iOS工程師。4年深耕移動端,餓了麼最早的一批Flutter玩家,重點參與了Flutter在蜂鳥團隊的業務開發落地、工程架構演進。

以下内容根據演講視訊以及PPT整理而成。

觀看回放

http://mudu.tv/watch/5817421
本次分享主要圍繞以下四個方面:
一、背景介紹  
            二、Flutter 在餓了麼的應⽤  
            三、基礎建設與沉澱  
            四、展望與規劃             

一、為什麼選擇使⽤ Flutter

Flutter的三大特點及架構

打開Flutter官網,映入眼簾的是Flutter的三大特點。首先是快速開發,毫秒級的繪制,亞秒級的熱加載,豐富的Widget,可以快速開發出使用者界面。二是富有表現力的UI,豐富的動效接口,流暢的滑動,支援iOS和Android兩種風格的Widget,可以組合出非常漂亮且靈活的界面。三是可媲美Native的運作性能,AOT模式下,Flutter代碼可以被編譯成ARM機器碼。下圖是Flutter架構圖,最底層是Flutter應用的各個平台,包括iOS、Android、Windows、MacOS、Ubuntu等。各個平台分别實作Embedder平台相關,如提供繪制的畫布,在Android和iOS上就是OpenGL Context, 還包含了線程設定和事件循環。再上一層是Flutter Engine層,使用了C++,包含DartVM,Skia 2D渲染等通用能力,還包含文字繪制等關鍵能力。最上一層是Flutter Framework層, 使用了Dart, 包含了Material和Cupertino兩種風格的Widget,以及一部分渲染邏輯,當然還包含架構與引擎之間的通信接口。Flutter的渲染基本與平台無關,平台隻是提供了畫布,這為Flutter的UI一緻性和運作一緻性提供了堅實的基礎。

Flutter原理

下圖右側是Flutter UI渲染的三棵樹,分别包含描述節點,虛拟節點和真實節點。Widget樹在運作期會生成Element樹和RenderObject樹,Widget樹在更新時會觸發Element樹的比對和更新,以觸發RenderObject樹的最小更新。Flutter與RN等方案最大的不同就在于Flutter的RenderObject是自己實作的,而RN是使用Native控件。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

用戶端研發方案對比

下圖給出了五種不同研發方案在四個次元上的對比,包括Native、Flutter、RN、Weex和H5。1)在性能方面,H5最差,Native最優。Flutter相比于RN,在AOT編譯模式下,由于沒有JS虛拟機,相對運作性能更高。而且,Flutter的渲染是直接對接底層的,RN還需要操作Native控件,有着非常高的通信成本。Flutter的頁面加載速度和流暢度都比RN更優,在測試中也是符合實際預期的。

在動态性方面,H5可以實時釋出,是以動态性最好。RN有JS虛拟機,可以一定程度上進行代碼的動态下方和執行。在AOT編譯下,Flutter的動态性可以認為是與Native差不多的,都非常弱。如果有此類需求,需要借助其它方式,如DSL等。

跨端一緻性可以分為UI一緻性和運作一緻性。H5借助于浏覽器規範,其UI一緻性是非常好的,運作一緻性也不錯。Native由于開發棧的不同,其RenderObject,API和實作都有很大的不同,是以跨端一緻性非常差。Flutter實作了渲染層,在Android和iOS上RenderObject的實作是一緻的。正是因為有了渲染層,平台相關性非常低,是以可以像H5一樣做到一處編寫,處處運作。

開發體驗方面,H5開發預覽調試都非常友善,Flutter的各個插件使用起來也比較友善,HotReload使得開發,調試和運作都可以節省不少時間。

總結起來,Flutter在動态性上有所诟病以外,性能可以與Native媲美,極強的跨端一緻性,還可以提供非常好的開發體驗。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

餓了麼的選擇

餓了麼對研發方案的期望是用最少的人,搬最快的磚,使用者無感覺!簡而言之是研發效率高,使用者體驗好。最後,餓了麼選擇了Flutter作為跨平台研發方案。Flutter具備完備好用的工具,如Pub,IDE插件,HotReload,優秀的跨端一緻性,富有表現力的UI,這些特點可以為開發調試和驗證工作節省很多時間,進而大大提升研發效率。跨端一緻性,豐富的UI,以及高性能使得Flutter開發的頁面與Native開發的頁面體驗無差别,而且Android與iOS可以保持高度的一緻性。另外,Flutter采用的自有渲染引擎在未來可擴充的餘地很多, Flutter是Google出品的,Flutter現在的社群也是非常火熱的,阿裡巴巴,騰訊,頭條等企業的很多App都使用了Flutter,可以說,Flutter有着非常廣闊的應用與技術前景。在動态性方面,目前大家已經有了很多解決方案,如DSL等。這點上還是以自己的需求出發,稍作補足即可。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

Flutter在餓了麼的應⽤

從2018年下半年到現在,餓了麼很多App上都已經使用了Flutter。其中至冠配送大概80%的頁面都使用了Flutter。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

下圖展示了餓了麼已經上線的Flutter頁面,涵蓋了大部分常見的場景,包括殘疾騎士驗證頁面、單量提升頁面、優惠券頁面、運單簽收頁面等。他們的頁面加載速度和滑動流暢度都基本與Native頁面相當。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

混合開發(舊)

Flutter代碼主要以Module的形式關聯到主工程,自然涉及到混合棧管理問題。Flutter頁面與Native頁面的切換主要有以下三種情況,Native切換Flutter、Flutter切換Flutter、Flutter切換Native。從2018年到2019年間,餓了麼也實作了自己的混合棧管理方案,Android和iOS的理念相似,這裡以iOS為例,關鍵在于Native側公用多個FlutterVC的FlutterView,解決FlutterVC不釋放問題。餓了麼在Flutter側做了一個LPDFRouter,記錄所有與Flutter頁面有關的URL。當從Native切換Flutter時,先打開Flutter Container VC,Flutter側的Navigator會push URL。當連續打開Flutter頁面時,Native容器不動,Flutter的Navigator直接push。當Flutter切換Native時,Native容器傳回按鍵綁定Dart方法,當上一個頁面是Flutter時,使用Native直接pop即可,如果上一個頁面是Native頁面,除了把目前URL pop掉,還需要将容器pop掉。下圖的混合棧管理方案基本上滿足了餓了麼的需求,但是方案的一半在Native,另一半在Flutter中,不太适合Tab非常複雜的情況。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

混合開發(新 - boost)

後來餓了麼使用了閑魚開發的Flutter boost作為混合棧管理方案,無論從Native還是Flutter打開一個頁面,都會先打開一個原生的容器,容器的生命周期ID會通過Channel傳到Flutter側,Flutter容器管理器會打開一個Flutter容器。Flutter容器管理器管理了多個Navigator,每個Navigator隻有一個Flutter頁面,一個Widget。Flutter容器與Native容器ID是一緻的。最新的Flutter boost使用了多個Flutter View的組合,不再複用單個Flutter View+截圖。可以發現Flutter boost對混合棧的管理是非常純粹的,都在Native側。在使用Flutter時,隻需要關注Native容器的生命周期即可,适合多Tab的Flutter頁面情況。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

研發/內建模式

Flutter工程與Native工程是如何組織的,下圖中的虛線框代表需要有Flutter環境。第一種是至冠模式Teemo,餓了麼最初就希望将至冠配送100% Flutter化。是以将Flutter工程與Native工程放在了同一個倉庫裡,Native工程通過ruby腳本關聯到Flutter工程。Native工程編譯時先編譯Flutter工程,将産物引進來,是以工程的開發、調試、測試、打包等都需要Flutter環境。下圖右側是蜂鳥模式,蜂鳥業務非常複雜,餓了麼考慮可能隻有部分的同學會開發Flutter業務,是以将Flutter環境與其它業務環境隔離開來。餓了麼做了一個Runner工程,同步了蜂鳥主工程的很多依賴,如登入、使用者管理、安全等。在Runner工程中把Flutter業務開發完之後,通過本地和遠端把Flutter産物抽取出來進行釋出,如App Framework、Flutter Framework、以及對應的原生代碼。釋出之後,蜂鳥工程會版本tag的形式依賴Flutter的原生産物,那麼隻開發Native業務的同學不需要Flutter環境。不同的團隊有不要的研發和內建模式,并不存在最佳實踐,适合自己的才是最關鍵的。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

品質&效率結果

從2018年到現在,餓了麼已經上線了很多Flutter頁面,頁面占比在不同團隊都是不同的。Crash方面,穩定下來後在0.01%級别。在流暢度方面,使用Flutter工具或使用線上回調函數計算,都可以達到50幀以上。而最大的驚喜是在提高研發效率上,在過去一年多的時間裡節省了100多開發人日。前期不是很熟練,1個人可以頂1.5個人,後期便熟練後,1個人可以頂1.8個人。

基礎建設與沉澱

控件庫

随着Flutter頁面的開發越來越多,餓了麼聯合UED做了基礎控件的規範和封裝,包括按鈕、TextFeild等。當基礎的控件建立完後,後續的Flutter頁面可以複用控件,進一步提高開發效率,同時使得不同頁面間的一緻性更好。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

基礎插件

Flutter是一個UI的開發架構,是以也不能免于使用Native能力,如定位、持久化。餓了麼在開發過程中也積累了很多基礎的插件,如Crash上報、推送處理、社交分享,還橋接了Native網絡庫發出Flutter請求,使得性能與體驗都保持一緻。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

dna - 背景與目标

随着Channel越來越多,小到擷取Native參數,大到擷取Native執行功能,使得Channel使用越來越不便,其痛點主要有以下三點,一是雙邊寫死。在Dart和Native兩邊針對Channel的名字,如方法名等參數做寫死,一旦出錯就會調用錯誤。二是隻能單次調用,Flutter調Native時隻能通過方法名比對到一個代碼塊。三是建立成本高,不單要編寫Channel代碼,有時還需要建立Plugin。為了解決以上問題,餓了麼也做了一些探索和實踐,即dna Plugin。在設計和實作dna Plugin時也有幾個對應的目标,首先是直接調用Native方法,不再使用Channel名或方法名等,同時Native側不再使用寫死。二是支援上下文調用,Dart可以直接調用Native代碼。三是無需建立Channel和Plugin。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

dna - 使用

下圖展示了dna快捷方法的使用,擷取了Android,iOS對應的版本号,以及系統平台的字元串。可以發現,使用了NativeObject作為Native變量,NativeObject的生命周期與Native的生命周期是一緻的。上一個方法的傳回值可以作為下一個方法的參數,即上下文調用。原生調用上與Native很像,支援鍊式文法。傳回值預設是最後一次context的的傳回值。下圖下層框裡是假想的代碼,dna執行Native方法時相當于執行了框中的方法。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

實際使用的dna代碼相對更簡單,下圖展示的是拆箱解碼的操作。iOS側調用了一個類來擷取執行個體,Android是直接調用了執行個體。iOS包含一個類一個方法,Android還需要一個dna method注解。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

dna - 原理

當上面的context中的Native Object不斷的Invoke 方法時,會形成一個個的InvocationNode,InvocationNode定義了方法名,參數以及傳回值。當context調用時,會轉換成一個context JSON,傳給Native。Native解析JSON,轉換成對應的一個個Invocation,被陸續調用。每個Native Object都對應一個ID。一個Invocation裡不但有調用值的ID,還調用了傳回值的ID,他們都會放在一個map裡,實作上下文關聯。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

dna在調用到一個Java方法時,需要一個dna method注解,主要是因為Android的release中做了代碼的混淆,無法通過類名和方法名定位。當一個方法加了Method注解時,可以掃描這個注解,生成代理類和代理方法。APT生成代理檔案中包含掃描注解、生成的代理方法、存儲的方法名和參數資訊。dna調用時先通過運作時注解比對到代理方法,然後通過owner調用到真正的對象方法。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

Channel VS. dna

dna不需要建立Plugin和Channel,而且由于dna是直接調用Native,是以所有寫死都沒有了,dna和Channel都不支援C函數,也不支援Native對象記憶體管理。目前dna傳遞JSON時還是使用Channel傳遞。很多情況下,隻需要調用小部分Native代碼,dna也無需建立Plugin及Channel,而且Native也不需要寫死。在蜂鳥團隊中,非常基礎的功能是使用Channel做的,稍微涉及業務的功能都使用dna。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

其他實踐

在餓了麼自身的實踐中,也做了其他的探索,如熱修複,雖然還沒有上線,但為Flutter boost提供了一些支援。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

參與共建 - 背景

AliFlutter的目标是共同打造基礎設施,制定标準,複用技術。通過培育集團各個BU Flutter生态,沉澱業務與技術。順便可以聯合對外産生凝聚的影響力。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

共建 - Pub Server

在Pub Server中,通過get和publish可以下載下傳一些庫。AliFlutter為了Pub庫的保密性,以及外部庫的下載下傳速度,也做了自己的Pub Server。與官方方案最大的不同是将Google Cloud存儲換成了阿裡雲OSS存儲,當查詢外部的庫時先看内部是否有緩存,沒有才去下載下傳外部的庫。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

共建 - Pub Dev(前端檢索)

Pub Dev是大家所使用的前端檢索頁面,可以查到已釋出的Plugin或Dart庫。既然有了内部的Pub Server,還需要做Pub Dev。Pub Server提供的檢索功能非常有限。餓了麼做了自己的元資訊資料庫,有一個定時任務可以定時讀取和解析Pub Server的産物資訊。如此Pub Dev才可以滿足各類檢索需求。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

共建 - 産物伺服器

AliFlutter也做了自己的産物伺服器,最初主要也是為了提高産物查詢速度。與Pub Server類似,依然是先檢查是否有内部的緩存,無需再去查詢外部産物,進而後面同學查詢的速度也更快了。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

共建 - 引擎工作流

同時,産物伺服器也可以作為引擎工作流。AliFlutter也對引擎做了一些修改,如圖檔優化和機器優化,再将Android和iOS産物,如編譯debug産物,上傳到産物伺服器中。整個引擎工作流是通過CI實作的。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

共建 - CI/CD

AliFlutter CI/CD的主要目标是打出包含Flutter代碼的App,以及Flutter Module下的産物。在标準的App建構流程pipeline之上,餓了麼加了很多Flutter編譯邏輯和産物的特化邏輯。先把App Framework和Flutter Framework都打出來,還需要把Flutter Plugin的代碼單獨打成Framework或aar,生成postsec,最後上傳并釋出。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

展望與規劃

Flutter給餓了麼帶來的最大的價值是業務落地的提速。在未來,餓了麼也會推進更多的業務使用Flutter進行開發,包括跨業務元件的開發,以及Flutter代碼的分包。圍繞業務,還需保證品質,目前對Flutter性能監控和優化都是不夠的,是以計劃在性能監控和優化上再進一步擴充。另外,在業務的狂奔過程中,控制Crash數量,做好包體積優化。此外,餓了麼也希望做進一步提高研發效率的工作,如擴大UI元件範圍,抽取基礎插件,通過與AliFlutter共建,做好基礎設施及效率工具,如掃碼開發等。最後,有剩餘時間的話還可以做一些探索性的工作,如動态化UI,Flutter Web,三端一體化開發,引擎定制,同時可以結合前端做一些思考和遐想。總結起來,餓了麼對Flutter的期待是保質提效,賦能業務。

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

關注「淘系技術」微信公衆号,一個有溫度有内容的技術社群~

Flutter 在餓了麼的應用與沉澱一、為什麼選擇使⽤ FlutterFlutter在餓了麼的應⽤基礎建設與沉澱展望與規劃

繼續閱讀