天天看點

GMTC2019|閑魚-基于Flutter的架構演進與創新

作者:閑魚技術-宗心

2012年應屆畢業加入阿裡巴巴,主導了閑魚基于Flutter的新混合架構,同時推進了Flutter在閑魚各業務線的落地。未來将持續關注終端技術的演變及趨勢

Flutter的優勢與挑戰

GMTC2019|閑魚-基于Flutter的架構演進與創新

Flutter是Google開源的跨端便攜UI工具包,除了具有非常優秀的跨端渲染一緻性,還具備非常高效的研發體驗,豐富的開箱即用的UI元件,以及跟Native媲美的性能體驗。由于它的衆多優勢,也使得Flutter成為了近些年來熱門的新技術。

通過以上的特點可以看出,Flutter可以極大的加速用戶端的研發效率,與此同時得到優秀的性能體驗,基于我的思考,Flutter會為以下團隊帶來較大的收益:

  • 中小型的用戶端團隊非常适合Flutter開發,不僅一端編寫雙端産出,還有效的解決了小團隊需要雙端人員(iOS:Android)占比接近1:1的限制,在項目快速推進過程中,能讓整個團隊的産能最大化。
  • App在Android市場占比遠高于iOS的團隊,比如出海東南亞的一些App,Android市場整體占比在90%以上,通過Flutter可以将更多的人力Focus在Android市場上,同時通過在iOS端較小的投入,在結果上達到買一送一的效果。
  • 以量産App為主要政策的團隊,不論是量産ToB的企業App,還是有針對性的産出不同領域的ToC的App的公司,都可以通過一端開發多端産出的Flutter得到巨大的産能提升。
GMTC2019|閑魚-基于Flutter的架構演進與創新

閑魚在以上的場景中屬于第一種場景,服務3億使用者的閑魚App的背後,開發資源投入很少,與競對相比,我們是一隻再小不過的團隊,在這種場景下,Flutter為閑魚業務的穩定發展以及提供更多的創新産品給予了很大的幫助。

但與此同時,Flutter在設計上帶來的優勢同時又會帶來新的問題。所有的新技術都是脫胎于老技術的,Flutter也不例外,其身上帶有很多Chrome的影子。我們再做一層簡化,如果我們認為Flutter是一個使用Dart語言的浏覽器容器,請大家思考一下兩個問題如何解決。

  • 如果在一個已經存在的App中加入Flutter,如何讓Native與Flutter進行無縫的銜接,同時保證互相開發之間的隔離性
  • 如果在Flutter的容器中,使用已有的Native UI元件,在Flutter與Native渲染機制不同的情況下,怎麼保證兩者的無縫銜接以及高性能。

閑魚的架構演進與創新

帶着上面兩個問題,我們來到閑魚場景下的具體Case以及解決方案的演進過程。

已有App+Flutter容器

GMTC2019|閑魚-基于Flutter的架構演進與創新

在這種情況下,閑魚需要考慮的是首先要考慮引入Flutter容器後的記憶體壓力,保證不要産生更多的記憶體溢出。與此同時我們希望能讓Flutter和Native之間的頁面切換是順暢的,對不同技術棧之間的同學透明。是以我們有針對性的進行了多次疊代。

在沒有任何改造的情況下以iOS為例,你可以通過建立新的FlutterViewController來建立一個新的Flutter容器,這個方案下,當建立多個FlutterViewController時會同時在記憶體中建立多個Flutter Engine的Runtime(雖然底層Dart VM依然隻有一個),這對記憶體消耗是相當大的,同時多個Flutter Engine的Runtime會造成每個Runtime内的資料無法直接共享,造成資料同步困難。

這種情況下,閑魚選擇了全局共享同一個FlutterViewController的方式保證了記憶體占用的最小化,同時通過基礎架構Flutter Boost提供了Native棧與Flutter棧的通信與管理,保證了當Native打開或關閉一個新的Flutter頁面時,Dart側的Navigator也做到自動的打開或關閉一個新的Widget。目前Google官方的提供的方案上就是參考閑魚早先的這個版本進行的實作的。

然而在這種情況下,如果出現如閑魚圖中所示多個Tab的場景下,整個堆棧邏輯就會産生混亂,是以閑魚在這個基礎上對Flutter Boost的方案進行了更新并開源,通過在Dart側提供一個BoostContainerManager的方式,提供了對多個Navigator的管理能力,如果打比方來看這件事,就相當于,針對Flutter的容器提供了一個類似WebView的OpenWindow的能力,每做一次OpenWindow的調用,就會産生一個新的Navigator,這樣開發者就可以自由的選擇是在Navigator裡進行Push和Pop,還是直接通過Flutter Boost新開一個Navigator進行獨立管理。

Flutter Boost目前已在github開源,由于閑魚目前線上版本隻支援Flutter 1.2的版本,是以需要支援1.5的同學等稍等,我們會在近期更新支援1.5的Flutter Boost版本。

Flutter頁面+Native UI

GMTC2019|閑魚-基于Flutter的架構演進與創新

由于閑魚是一個閑置交易社群,是以圖檔和視訊相對較多,對圖檔視訊的線上性能以及記憶體占用有較嚴格的要求。目前Flutter已提供的幾種方案中(Platform View以及Flutter Plugin),不論是對記憶體的占用還是整個的線上流暢度上還存在一定的問題,這就造成了當大部分同學跟閑魚一樣實作一個複雜的圖文Feed推薦場景的時候,非常容易産生記憶體溢出。而實際上,閑魚在以上的場景下有針對性的做出了較大的優化。

在整個的Native UI到Flutter渲染引擎橋接的過程中,我們選用了Flutter Plugin中提供的FlutterTextureRegistry的能力,在去年上半年我們優先針對視訊的場景進行了優化,優化的思路主要是針對Flutter Engine底層的外接紋理接口進行修改,将原有接口中必須傳入一個PixelBuffer的記憶體對象這一限制做了擴充,增加一個新的接口保證其可以傳入一個GPU對象的TextureID。

如圖中所示,優化後的整個鍊路Flutter Engine可以直接通過Native端已經生成好的TextureID進行Flutter側的渲染,這樣就将鍊路從Native側生成的TextureID->copy的記憶體對象PixelBuffer->生成新的TextureID->渲染,轉變為Native側生成的TextureID->渲染。整個鍊路極大的縮短,保證了整個的渲染效率以及更小的記憶體消耗。閑魚在将這套方案上線後,又嘗試将該方案應用于圖檔渲染的場景下,使得圖檔的緩存,CDN優化,圖檔裁切等方案與Native歸一,在享受已有集團中間件的性能優化的同時,也得到了更小的記憶體消耗,方案落地後,記憶體溢出大幅減少。

目前該方案由于需要配合Flutter Engine的修改,是以暫時無法提供完整的方案至開源社群,我們正在跟google積極溝通整個修改方案,相信在這一兩個月内會将試驗性的Engine Patch開源至社群,供有興趣的同學參考。

複雜業務場景的架構創新實踐

GMTC2019|閑魚-基于Flutter的架構演進與創新

将以上兩個問題解決以後,閑魚開始了Flutter在業務側的全面落地,然而很快又遇到新的問題,在多人協作過程中:

  • 如何提供一些标準供大家進行參考保證代碼的一緻性
  • 如何将複雜業務進行有效的拆解變成子問題
  • 如何保證更多的同學可以快速上手并寫出性能和穩定性都不錯的代碼
GMTC2019|閑魚-基于Flutter的架構演進與創新

在方案的前期,我們使用了社群的Flutter Redux方案,由于最先落地的詳情,釋出等頁面較為複雜,是以我們有針對性的對View進行了元件化的拆分,但由于業務的複雜性,很快這套方案就出現了問題,對于單個頁面來說,State的屬性以及Reducer的數量都非常多,當産生新需求堆疊的時候,修改困難,容易産生線上問題。

針對以上的情況,我們進行了整個方案的第二個疊代,在原有Page的基礎上提供了Component的概念,使得每個Component具備完整的Redux元素,保證了UI,邏輯,資料的完整隔離,每個Component單元下代碼相對較少,易于維護和開發,但随之而來的問題是,當頁面需要産生資料同步時,整個的複雜性飙升,在Page的次元上失去了統一狀态管理的優勢。

GMTC2019|閑魚-基于Flutter的架構演進與創新

在這種情況下閑魚換個角度看端側的架構設計,我們參考React Redux架構中的Connect的思想,移除掉在Component的Store,随之而來的是新的Connector作為Page和Component的資料聯通的橋梁,我們基于此實作了Page State到Component State的轉換,以及Component State變化後對Page State的自動同步,進而保證了将複雜業務有效的拆解成子問題,同時享受到統一狀态管理的優勢。與此同時基于新的架構,在統一了大家的開發标準的情況下,新架構也在底層有針對性的提供了對長清單,多清單拼接等case下的一些性能優化,保證了每一位同學在按照标準開發後,可以得到相對目前市面上其他的Flutter業務架構相比更好的性能。

目前這套方案Fish Redux已經在github開源,目前支援1.5版本,感興趣的同學可以去github進行了解。

研發智能化在閑魚的應用

閑魚在去年經曆了業務的快速成長,在這個階段上,我們同時進行了大量的Flutter的技術改造和更新,在嘗試新技術的同時,如何能保證線上的穩定,線下的有更多的時間進行新技術的嘗試和落地,我們需要一些新的思路和工作方式上的改變。

GMTC2019|閑魚-基于Flutter的架構演進與創新

以我們日常工作為例,Flutter的研發同學,在每次開發完成後,需要在本地進行Flutter産物的編譯并上傳到遠端Repo,以便對Native同學透明,保證日常的研發不受Flutter改造的幹擾。在這個過程中,Flutter側的業務開發同學面臨着很多打包上傳更新同步等繁瑣的工作,一不小心就會出錯,後續的排查等讓Flutter前期的開發變成了開發5分鐘,打包測試2小時。同時Flutter到底有沒有解決研發效率快的問題,以及同學們在落地過程中有沒有Follow業務架構的标準,這一切都是未知的。

GMTC2019|閑魚-基于Flutter的架構演進與創新

在痛定思痛以後,我們認為資料化+自動化是解決這些問題的一個較好的思路。是以我們首先從源頭對代碼進行管控,通過commit,将代碼與背景的需求以及bug一一關聯,對于不符合要求的commit資訊,不允許進行代碼合并,進而保證了後續資料報表分析的資料源頭是健康的。

在完成代碼和任務關聯後,通過webhook就可以比較輕松的完成後續的工作,将每次的commit有效的關聯到我們的持續內建平台的任務上來,通過閑魚CI工作平台将日常打包自動化測試等流程變為自動化的行為,進而極大的減少了日常的工作。粗略統計下來,在去年自動化體系落地的過程中單就自動打Flutter包上傳以及觸發最終的App打包這一流程就讓每位同學每天節省一個小時以上的工作量,效果非常明顯。另外,基于代碼關聯需求的這套體系,可以相對容易的建構後續的資料報表對整個過程和結果進行細化的分析,用資料驅動過程改進,保證新技術的落地過程的收益有理有據。

總結與展望

回顧一下上下文

  • Flutter的特性非常适合中小型用戶端團隊/Android市場占比較高的團隊/量産App的團隊。同時由于Flutter的特性導緻其在混合開發的場景下面存在一定劣勢。
  • 閑魚團隊針對混合開發上的幾個典型問題提供了對應的解決方案,使整個方案達到上線要求,該修改會在後續開放給google及社群。
  • 為全面推動Flutter在業務場景下的落地,閑魚團隊通過多次疊代演進出Fish Redux架構,保證了每位同學可以快速寫出相對優秀的Flutter代碼。
  • 新技術的落地過程中,在過程中通過資料化和自動化的方案極大的提升了過程中的效率,為Flutter在閑魚的落地打下了堅實的基礎。

除了本文提及的各種方案外,閑魚目前還在多個方向上發力,并對針對Flutter生态的未來進行持續的關注,分享幾個現在在做的事情

  • Flutter整個上層基礎設施的标準化演進,混合工程體系是否可以在上層完成類似Spring-boot的完整體系構架,幫助更多的Flutter團隊解決上手難,無行業标準的問題。
  • 動态性能力的擴充,在符合各應用商店标準的情況下,助力業務鍊路的營運效率提升,保證業務效果。目前閑魚已有的動态化方案會後續作為Fish-Redux的擴充能力提供動态化元件能力+工具鍊體系。
  • Fish-Redux + UI2Code,打通代碼生成鍊路和業務架構,保證在團隊标準統一的情況下,将UI工作交由機器生成。
  • Flutter + FaaS,讓用戶端同學可以成為全棧工程師,通過前後端一體的架構設計,極大的減少協同,提升效率。

讓工程師去從事更多創造性的工作,是我們一直努力的目标。閑魚團隊也會在新的一年更多的完善Flutter體系的建設,将更多已有的沉澱回饋給社群,幫助Flutter社群一起健康成長。

繼續閱讀