Flutter 剛出現在大家視野裡的時候,首先的反應是否有動態化熱更新的支援,不過目前Flutter的動態化熱更新只限於除錯Debug的階段,在生產打包時是不支援這一個特性的,這主要與Flutter的編譯模式有關。在Debug除錯階段,Flutter是以JIT(即時編譯)模式執行,而生產打包後,是以AOT(事前編譯)執行。
JIT 與 AOTJIT典型的例子就是瀏覽器的V8引擎,可以動態執行Javascript程式碼,Flutter的JIT與V8執行模式相同,在這種模式下,可以實現和Web前端一樣“所見即所得”的開發方式,大幅提高開發的效率,缺點也同樣很明顯,由於要支援動態化,需要額外執行很多程式碼,導致執行效能嚴重下降,所以這種模式也僅限於開發階段使用。那麼另外AOT模式,這種模式與C/C++類似,執行前需要編譯成二進位制程式碼,優點顯而易見,執行效率很高,缺點就是不支援動態化,每次程式碼改動都需要重新編譯才能執行。
Flutter動態化的方案思路了解了Flutter的JIT和AOT的編譯模式,就明白想要在生產環境下支援動態化基本不太可行了,除非後面Flutter的JIT引擎優化的足夠好,可以用到生產環境中。不過在那之前,我們想要實現這一特性的話,就得另闢蹊徑了。
首先在網上搜索了相關資料做了一番調研,目前支援動態化的移動端方案有HyBrid和ReactNative,不過兩種技術使用的開發語言都是Javascript,也是利用了Javascript的動態化特性,前文已經說過Flutter生產環境下是以AOT方式執行的,所以HyBrid和RN的方案對我們不適合。
另外一個方案是在Flutter Android 上實現的熱更新,是通過替換AOT編譯後二進位制檔案實現的,從原理上這個方案可以滿足我們的需求,不過還是有兩個不足的地方:
該方案可行的前提是Flutter的AOT編譯檔案可讀寫,其中有一個風險,如果後期Google限制了AOT編譯檔案的許可權,無法進行Hack替換的話,那這個方案就徹底作廢掉了。出於安全性考慮,這個限制很可能會出現。我們選擇Flutter的一個重要原因是支援多終端執行,Android & iOS,後面還會接著支援Windows、MacOS等,如果這個方案只是執行在Android上的話,對我們來說價值不是很大。偶然看到閒魚技術團隊的一篇文章,提到了第三種方案 《 Flutter動態化的方案對比及最佳實現 》 ,思路是利用Dart語言宣告式程式設計特性,以及Flutter UI的構建方式,通過將模板程式碼編譯成AST描述檔案,然後在App側執行時動態解析這個AST描述檔案,構造成Widget物件,然後通過 setState() 重新渲染UI。通過比較,這個方案目前是最合適的,沒有平臺差異性,各平臺可通用,並且沒有改變AOT的編譯模式,只是在AOT之上實現了一個模擬JIT的輕量級動態執行引擎。
在我們的Flutter業務程式碼中將同時包含Flutter AOT和AST Runtime 的部分, 其實AST Runtime 方案也不是十全十美,首先要考慮效能,我們要解析的AST不能太複雜,其次該方案有適用的場景,使用前需要根據公司產品的情況整理好標準封裝的元件,這樣直接在AST中進行定義,也可以減少AST解析的壓力,不用過多考慮多種佈局組合的情況,也可以減少我們編寫DSL模板程式碼的成本。
如何實現理論上我們分析AST Runtime的方案在技術和場景上行得通,接下來就要考慮如何實現這個方案,把方案進行拆解,我們需要完成以下4個階段的任務:
實現DSL模板程式碼編譯為AST描述檔案實現App側AST Runtime輕量級引擎實現AST 管理後臺,儲存AST描述檔案實現App側AST 快取管理,並設計與後臺的同步策略重點在於第1、2兩個階段,如何將DSL模版與AST Runtime 結合是個難點,對此,我們需要進一步分析我們的產品,以我們公司產品為例,我們的產品叫 新核雲 ,是專注於製造業行業的MES&ERP系統,App端的主要功能就是和後端各種Api的互動,也是ToB類App顯著特點。這樣App主要的功能形態就簡單多了,我們之前在架構Flutter 的時候採用官方推薦的BLoC模式,將UI與業務解耦開,對於AST動態化,我們遵循同樣的思路:
這樣我們可以進一步區分要完成的工作:
AST Widget 負責解析AST並構造WidgetAST BLoC 則動態解析業務邏輯將上圖進一步細化:
再上圖中,我們能看到AST Widget 真正與之互動的是AST Runtime,也就是說,對於業務邏輯這部分是動態執行的,需要通過AST Runtime開放的介面來動態執行相應的業務邏輯。
最後通過流程圖再把我們的思路梳理一下:
整體的動態化方案思路大致就是這樣子,先寫到這裡。