詳解 ROMA 中複雜圖表的渲染實現 - 新闻 详情

一、背景

ROMA承接很多複雜圖表的渲染需求,在京東金融APP內,特別是首頁首屏的圖表,對圖表渲染的及時性要求很高。近期業務反饋頻繁重啓時,首頁的黃金走勢圖偶現渲染不出的問題,通過梳理圖表的渲染流程,對緩存策略、視圖加載和渲染過程進行了重構,確保渲染成功率,提升了渲染速度以及補充了異常重試的功能。

二、使用場景分析

京東金融App內有很多使用複雜圖表的業務場景,以下截取部分場景。下圖分別是黃金歷史金價的走勢圖、用户購買的基金的收益走勢圖、小金庫的收益柱狀圖、用户投資診斷的雷達圖、省錢賬單和AI助手賬單趨勢柱狀圖。

 

可見,金融App內圖表的使用具備種類豐富、數據信息量大,定製程度高、交互頻繁等特點,經調研發現,Apache ECharts 是一個基於 JavaScript 的功能強大的開源可視化圖表庫,被廣泛應用於數據分析、監控系統、報表工具等領域。並且支持定製支持的圖表類型,可降低圖標庫的體積和提升圖標庫的加載速度。由於原生端並沒有類似的圖表庫,因次決定在 ROMA 中引入 ECharts 來承接複雜圖表的顯示需求 。

三、重構過程分析

1、原理分析

ROMA 對外提供 echarts 標籤,內部依賴提前加載了 echarts.js 庫的 WebView,將圖表數據交給準備好環境的 webveiw,達到渲染圖表的目的。這裏有一個重要的前提就是成功加載了echarts.js 庫的 WebView 才具備快速渲染各類圖表的能力。並且需要提前打通 ROMA 與 Native,Native 與 WebView 之間的數據通訊,保證數據在三端之間的順暢流轉。

為此自定製了 JRTransEchartsWebView 專門用於渲染 echarts 數據,JRTransEchartsComponent 作為標籤實現在承接 webview 和 jue 環境的數據傳遞和業務邏輯處理。以下類圖展示了各主要類對象之間的相互關係。

 
首先打通 Native 和 WebView 的數據交互,原生端在創建 WebView 的時候就向其環境中注入 window 的 message 事件監聽,攔截指定類型的事件,獲取從 WebView 環境中發來的數據。
NSString *jsStrring = @"window.addEventListener('message', (e) => { \
                        var customDict = {'function':'jdttransWindowEventDispatch',\ 
                                          'careParamDict':{'data': e.data, 'origin': e.origin}};\ 
                        window.webkit.messageHandlers.JDTTransEchartsHandler.postMessage(customDict);})";
NSString *jscode = [NSString stringWithFormat:jsStrring];
WKUserScript *script = [[WKUserScript alloc]initWithSource:jscode injectionTime:WKUserScriptInjectionTimeAtDocumentStart forMainFrameOnly:YES];
[webView.configuration.userContentController addUserScript:script];

Native 向 WebView 發送數據通過 evaluateJavaScript 的方式,向JS環境中輸入數據:

NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:@{@"type":@"message"}]; 
[dict jdd_setObjectCheck:message forKey:@"data"]; 
[dict jdd_setObjectCheck:@"*" forKey:@"origin"]; 
NSString *dictString = [dict jdd_JSONString]; 
NSString *jsString = [NSString stringWithFormat:@"javascript:(function (){ \
                                                  var event = new MessageEvent('message', %@);\ 
                                                  window.dispatchEvent(event);})()",dictString]; 
[webView evaluateJavaScript:jsString completionHandler:nil];

JUE 和 Native 之間的通訊通道在初始化 ROMA 的 SDK 時就已經建立,Native 和 webview 的數據交互也通過 postMessage 的方式建立,如下圖所示:



以上,JUE 通過調用 call native 將數據發送到 Native,Native 通過 window.dispatchEvent 的方式將數據傳遞給 WebView。而圖表上的手勢交互等事件 WebView 通過 postMessage 的方式發送,在 Native 監聽指定類型的 message 獲取數據,通過處理並通過 fire event 觸發到 JUE。

2、緩存的設計

對於 echarts 渲染圖表數據,大致經歷以下過程,創建 webview,加載 echarts.js 渲染庫,資源開始加載 ,資源成功加載,最後渲染圖表數據,這幾個過程中,除了最後一步渲染數據,前面的步驟都可以提前做,越早完成前面的工作,越能提升圖表渲染的效率。



為此,設計了可重複利用的並可自動擴容的 WebView 緩存容器 JRTransEchartsCache。在App啓動階段緩存 min 個 WebView 保存至緩存列表 cacheArray 中,並持續跟蹤 WebView 加載 echarts.js 的階段,確保在成功加載了 echarts.js 資源後再開始渲染圖表數據。而對於過早的圖表渲染指令,則先保存至指定標籤的指令緩存列表 eventArray 中,待成功加載後,再渲染 eventArray 中的數據。

而對於 WebView 加載 echarts.js 資源失敗的情況,也加入了失敗重試的邏輯,當業務端發起數據渲染時會檢查環境狀態,而觸發 echarts.js 的重新加載。

隨着 WebView 使用,其狀態被標記為 using,並根據使用的情況,自動觸發 cacheArray 中的 WebView 擴容,最大擴容至 max 個。對於從詳情頁返回而釋放的圖表,其 WebView 將會被標記為 free ,可提供為其他的圖表視圖使用。 當使用圖表的業務持續增多,當 cacheArray 中的 WebView 都被使用後,則新創建的 WebView 不再加入 cacheArray,遵循當次獲取,當次創建,使用完成,就地銷燬的原則。

3、渲染流程

下圖記錄了從 App 啓動到業務創建 echarts 圖表,到業務退出,到 App 退出期間,融合了緩存的初始化以及自動擴容,包括在 webview 加載 echarts.js 資源的不同階段對渲染指令的處理,以及視圖銷燬後的內存處理等場景下的處理流程。

 

四、效果驗證

為了更直觀的展示圖表在業務上的使用場景,使用重構後的圖表標籤渲染柱狀圖、條形圖、折線圖、餅圖和組合圖表,渲染的效果和操作都很流暢(由於文檔的限制,對以下視頻做了降頻和清晰度的處理),效果如下:



五、總結

通過此次圖表的重構,在App冷啓時,以 iPhoneXS Max 設備為例,首頁首屏渲染圖表數據的時間平均縮短了200ms;冷啓首頁首屏圖表的渲染成功率,由之前的平均90%提升至接近100%;非首頁首屏的圖表渲染幾乎零延遲;對於異常情況導致的環境初始化失敗的問題,也在接受渲染指令時自檢渲染環境並重啓環境初始化,自動恢復數據的渲染。如果你也對圖表渲染或者對跨端框架 ROMA 感興趣,可留言交流。