背景
在端上為了提升App的靈活性, 快速解決萬變的業務需求,開發者們探索了多種解決方案,如PhoneGap ,React Native ,Weex等,但在Flutter生態還沒有好的解決方案。未來閑魚都會基於Flutter 來跨端開發,如果突破發版周期,在不發版的情況下,完成業務需求,同時能兼容性能體驗,無疑是更快的響應了業務需求。因此我們需要探索在Flutter生態下的動態化。
方案選擇
借鑒Android 和Ios上的動態性方案,我們也思考了多種Flutter動態性方案。
1.下載替換Flutter編譯產物
下載新的Flutter編譯產物,替換 App 安裝目錄下的編譯產物,來實現動態化,這在Android 端是可行的,但在Ios 端不可行。我們需要雙端一體的解決方案,所以這不是最好選擇。
2.類似React Native 框架
我們先來看看React Native 的架構
React Native 要轉為android(ios) 的原生組件,再進行渲染。
用React Native的設計思路,把XML DSL轉為Flutter 的原子widget組件,讓Flutter 來渲染。技術上說是可行的,但這個成本很大,這會是一個龐大的工程,從投入產出比看,不是很好的選擇
3.頁面動態組件框架
由粗粒度的Widget組件動態拼裝出頁面,Native端已經有很多成熟的框架,如天貓的Tangram,淘寶的DinamicX,它在性能、動態性,開發周期上取得較好平衡。關鍵它能滿足大部分的動態性需求,能解決問題。
三種方案的比較圖表如:
根據實際動態性需求,從兩端一致性,和性能,成本,動態性考慮,我們選擇一個折中方案,頁面動態組件的設計思路是一個不錯的選擇。
頁面動態組件框架
在Flutter上使用粗力度的組件動態拼裝來構建頁面,需要一整套的前后端服務和工具。本文我們重點介紹前端界面渲染引擎過程。
語法樹的選擇
Native端的Tangram ,DinamicX等框架他們有個共同點,都是Xml或者Html 做為DSL。但是Flutter 是React Style語法。他自己的語法已經能很好的表達頁面。無需要自定義的Xml 語法,自定義的邏輯表達式。用Flutter 源碼做為DSL 能大大減輕開發,測試過程,不需要額外的工具支持。
所以選擇了Flutter 源碼作為DSL,來實現動態化。
如何解析DSL
Flutter源碼做為DSL,那我們需要對源碼進行很好的解析和分析。Flutter analyzer給了我們一些思路,Flutter analyzer是一個代碼風格檢測工具。它使用package:analyzer來解析dart 源碼,拿到ASTNode。
看下Flutter analyze 源碼結構,它使用了dart sdk 里面的 package:analyzer
dart-sdk: analysis_server: analysis_server.dart handleRequest(Request request) analyzer: parseCompilationUnit() parseDartFile parseDirectives
Flutter analyze 解析源碼得到ASTNode過程。
插件或者命令對analysis server發起請求,請求中帶需要分析的文件path,和分析的類型,analysis_server經過使用 package:analyzer 獲取 commilationUnit (ASTNode),再對astNode,經過computer分析,返回一個分析結果list。
同樣我們也可以把使用 package:analyzer 把源文件轉換為commilationUnit (ASTNode),ASTNode是一個抽象語法樹,抽象語法樹(abstract syntax tree或者縮寫為AST)是源代碼的抽象語法結構的樹狀表現形式.
所有利用抽象語法樹能很好的解析dart 源碼。
解析渲染引擎
下面重點介紹渲染模塊
架構圖:
1.源碼解析過程
1.AST樹的結構
如下面這段Flutter組件源碼:
import 'package:flutter/material.dart'; class FollowedTopicCard extends StatelessWidget { @override Widget build(BuildContext context) { return new Container( padding: const EdgeInsets.fromLTRB(12.0, 8.0, 12.0, 0.0), child: new InkWell( child: new Center( child: const Text('Plugin example app'), ), onTap: () {}, ), ); } }
它的AST結構:
從AST結構看,他是有規律的.
2.AST 到widget Node
我們拿到了ASTNode,但ASTNode 和widget node tree 完全是兩個不一樣的概念,
需要遞歸ASTNode 轉化為 widget node tree.
widget Node 需要的元素
用Name 來記錄是什么類型的widget
widget的arguments放在 map里面
widget 的literals 放在list 里面
widget 的children 放在lsit 里面
widget 的觸發事件 函數map里面
widget node 加fromjson ,tojson 方法
可以在遞歸astNode tree 時候,識別InstanceCreationExpression來創建一個widget node。
2.組件數據渲染
框架sdk 中注冊支持的組件,組件包括:
a.原子組件:Flutter sdk 中的 Flutter 的widget
b.本地組件:本地寫好到一個大顆粒的組件,卡片widget組件
c.邏輯組件:本地包裝了邏輯的widget組件
d.動態組件:通過源碼dsl動態渲染的widget
具體代碼如下:
const Map<String, CreateDynamicApi> allWidget = <String, CreateDynamicApi>{ 'Container': wrapContainer, …………. } static Widget wrapContainer(Map<String, dynamic> pars) { return new Container( padding: pars['padding'], color: pars['color'], child: pars['child'], decoration: pars['decoration'], width: pars['width'], height: pars['height'], alignment: pars['alignment'] ); }
一般我們通過網絡請求拿到的數據是一個map。
比如源碼中寫了這么一個 '${data.urls[1]}'
AST 解析時候,拿到這么一個string,或者AST 表達式,通過解析它 ,肯定能從map 中拿到對應的值。
3.邏輯和事件
a.支持邏輯
Flutter 概念萬物都是widget ,可以把表達式,邏輯封裝成一個自定義widget。如果在源碼里面寫了if else,變量等,會加重sdk解析的過程。所以把邏輯封裝到widget中。這些邏輯widget,當作組件當成框架組件。
b.支持事件
把頁面跳轉,彈框,等服務,注冊在sdk里面。約定使用者僅限sdk 的服務。
4.規則和檢測工具
a.檢測規則
需要對源碼的格式制定規則。比如不支持 直接寫if else ,需要使用邏輯wiget組件來代替if else 語句。如果不制定規則,那ast Node 到widget node 的解析過程會很復雜。理論上都可以解析,只要解析sdk 夠強大。制定規則,可以減輕sdk的解析邏輯。
b.工具檢測
用工具來檢測源碼是否符合制定的規則,以保證所有的源碼都能解析出來。
性能和效果
幀率大於50fps,體驗上看比weex相同功能的頁面更加流暢,Samsung galaxy s8上,感覺不出組件是通過動態渲染的.
數據結構
服務端請求到的數據,我們可以約定一種格式如下:
class DataModel { Map<dynamic, dynamic> data; String type; }
每個page 都是由組件組成的,每個組件的數據都是 DataModel來渲染。
根據type 來找到對應的模版,模版+data,渲染出界面。
動態模版管理模塊
我們把Widget Node Tree 轉換為一個組件Json模版,它需要一套管理平台,來支持版本控制,動態下載,升級,回滾,更新等。
框架的邊界
該框架是通過組件的組裝,組件布局動態變更,頁面布局動態變更來實現動態化。所以它適合運營變化較快的首頁,詳情,訂單,我的等頁面。一些復雜的邏輯需要封裝在組件里面,把組件內置到框架中,當作本地組件。框架側重於動態組件的組裝,而引擎對於源碼復雜的邏輯表達式的解析是弱化的。
后續拓展
1.和UI自動化的結合
UI自動化
,前面已經有文章介紹。UI自動化工具生成組件,再組件轉為模版,動態下發,來快速解決運營需求。
2.國際化的支持
App在不同國家會有不同的功能,我們可以根據區域,來動態拼裝我們的頁面。
3.千人千面
根據不同的人群,來動態渲染不一樣的界面。
總結
本文介紹動態化方案的渲染部分。該方案都在初探階段,還有很多需要完善,后續會繼續擴展和修改,等達到開源標准后,會考慮開源。動態方案是一個后端前端一體的方案,需要一整套工具配合,后續會有文章繼續介紹整體的動態化方案。
原文鏈接
本文為雲棲社區原創內容,未經允許不得轉載。