作者:京東科技 胡大海
前言
動態(tài)化跨端框架(后文統(tǒng)稱“動態(tài)化”)是一個由京東金融大前端團(tuán)隊全自主研發(fā)的,一份代碼,可以在HarmonyOS、iOS、Android、Web四端運行的跨平臺解決方案。在研發(fā)團(tuán)隊使用后可大幅降低研發(fā)人力成本;為業(yè)務(wù)提供實時觸達(dá)、A/B觸達(dá)等能力以提升業(yè)務(wù)投放效率;同時保障了C端用戶優(yōu)秀的用戶體驗。
一、動態(tài)化跨端框架原理介紹
??
通過上圖,我們先了解一下動態(tài)化跨端框架在iOS、Android等多個平臺實現(xiàn)跨端的原理:
① 業(yè)務(wù)層:業(yè)務(wù)代碼經(jīng)過打包后形成business.js發(fā)布到云端,被Harmony、iOS、Android、H5四端共用。
② 虛擬機(jī)層:虛擬機(jī)的核心職責(zé)是運行js代碼,這也是跨平臺框架的基礎(chǔ),在iOS使用系統(tǒng)內(nèi)置的JSCore,在Android使用V8,在瀏覽器使用Webkit,在鴻蒙系統(tǒng)我們依然需要一個能夠運行js代碼的虛擬機(jī)。
③ 通訊層:在iOS和Android端使用了json數(shù)據(jù)格式進(jìn)行通訊數(shù)據(jù)的傳輸,在鴻蒙系統(tǒng)也可以使用該方案。
④ 渲染層:使用各個系統(tǒng)的系統(tǒng)組件進(jìn)行UI元素的渲染。
二、基于方舟虛擬機(jī)的方案探索
1、方舟字節(jié)碼概念
方舟字節(jié)碼(Ark Bytecode),是由方舟編譯器編譯ArkTS/TS/JS生成的,提供給方舟運行時解釋執(zhí)行的二進(jìn)制文件,字節(jié)碼中的主要內(nèi)容是方舟字節(jié)碼指令。
2、在方舟虛擬機(jī)中運行JS
方舟虛擬機(jī)不能直接運行當(dāng)前在V8中運行的js代碼,但是能夠執(zhí)行方舟字節(jié)碼,所以我們可以借助鴻蒙提供的工具將js代碼轉(zhuǎn)化為方舟字節(jié)碼,這樣就能利用鴻蒙系統(tǒng)的方舟虛擬機(jī)執(zhí)行我們的js代碼了。
??
3、存在的問題
3.1、業(yè)務(wù)無法熱更新
在iOS和Android端,業(yè)務(wù)可以隨時打包后在云端發(fā)布新的版本,借助于JSCore或者V8就可以直接運行新的版本的js,這樣就支持了業(yè)務(wù)的熱更新發(fā)布。但在鴻蒙系統(tǒng)上,華為基于安全考慮,business.abc這樣的字節(jié)碼文件不支持動態(tài)下發(fā),需要內(nèi)置到APP中,這樣就失去了業(yè)務(wù)熱更新的能力。
3.2、單線程性能問題
在其他兩端我們是開啟了一個單獨的JS線程,進(jìn)行business.js文件的執(zhí)行,但是如果我們使用方舟虛擬機(jī)執(zhí)行business.js轉(zhuǎn)換來的business.abc的時候,其實是在方舟虛擬機(jī)的UI主線程運行了這個文件。在其他兩端js文件在JS線程執(zhí)行的時候,UI渲染和交互是并行不受影響的,但是在方舟虛擬機(jī)單線程下abc文件的執(zhí)行和UI渲染&交互變成了串行,這樣必然會嚴(yán)重影響頁面渲染速度和交互的流暢度。
業(yè)務(wù)不能熱更新以及單線程性能不佳等問題的存在,我們決定使用另一種方案-V8虛擬機(jī)。
三、基于V8虛擬機(jī)的方案落地
1、在V8虛擬機(jī)中運行JS
如果能把V8移植到鴻蒙系統(tǒng)中,我們就可以像其他兩端一樣使用多線程并且能實現(xiàn)業(yè)務(wù)熱更新等特性,但是V8是一個近千萬級代碼的龐大倉庫,需要掌握CMake、Clang、LLVM、Ninja等一系列交叉編譯知識(嵌入式范疇),對于應(yīng)用開發(fā)者是一個巨大的挑戰(zhàn),雖然我們已經(jīng)掌握了V8移植到鴻蒙的技術(shù),但從包大小、穩(wěn)定性、兼容性、維護(hù)成本等維度看,華為廠商內(nèi)置V8是一個具有長期收益的重大事項,通過和華為持續(xù)溝通,最終華為將V8內(nèi)置到了操作系統(tǒng),業(yè)界所有類動態(tài)化框均可直接使用內(nèi)置V8虛擬機(jī)進(jìn)行跨端框架的適配。
??
2、高性能核心方案
2.1、多線程架構(gòu)
多線程是提高程序性能最直接、最有效的手段之一,借助于鴻蒙系統(tǒng)內(nèi)置的V8虛擬機(jī),我們就能像iOS、Android兩端一樣使用三線程模型完成動態(tài)化跨端框架在鴻蒙系統(tǒng)的渲染過程。
JS線程負(fù)責(zé)將業(yè)務(wù)代碼解析為一顆虛擬Dom樹、發(fā)出渲染命令、處理業(yè)務(wù)邏輯等,通過接口定義的橋方法發(fā)送給組件線程進(jìn)行處理。我們以添加一個點擊按鈕節(jié)點為例,JS線程會通過“添加節(jié)點”這個接口以JSON描述的方式,將信息傳遞給組件線程,組件線程根據(jù)JSON描述將這個點擊按鈕節(jié)點添加到組件樹中,然后觸發(fā)UI線程創(chuàng)建系統(tǒng)組件,比如在鴻蒙系統(tǒng)會創(chuàng)建一個ArkTS的按鈕組件,在iOS系統(tǒng)會創(chuàng)建一個UIButton組件。
UI線程負(fù)責(zé)用戶頁面滑動、點擊事件等交互行為,當(dāng)發(fā)生比如用戶點擊事件后,同樣通過接口定義的橋方法“調(diào)用JS”,將點擊事件傳遞給JS線程進(jìn)行處理,緊接著繼續(xù)處理UI線程的任務(wù),這樣UI線程的交互效率就高了,充分保障了用戶良好的操作體驗。
//JSON描述示例 { "type":"btn", "value":"按鈕", "childrens":[], "id":"238346e885ee", "style":{ "width":"66px" }, "attr":{ "text-color":"#FFFFFFFF" }, "event":{ "onclick":"myclick()" } }
2.2、JSI技術(shù)引入
通訊橋存在的問題
動態(tài)化基于三個線程并行運行的方式,使其渲染性能已經(jīng)接近于原生的渲染性能,但是在一些頻繁通訊場景,通訊橋會“堵塞”,比如當(dāng)業(yè)務(wù)需要監(jiān)聽一個頁面的滑動而改變另外一個元素背景色的透明度,那么JS線程大部分時間在處理接收列表滑動距離,改變元素背景色透明度這個任務(wù)中,其他任務(wù)的執(zhí)行會被嚴(yán)重影響。另外JSON數(shù)據(jù)傳輸?shù)男蛄谢头葱蛄谢^程也會帶來很大的線程性能損耗。
??
解決方案-JSI
之前使用通訊橋的一個主要原因就是 C++ 中的函數(shù)沒辦法完整映射到 JavaScript 中,讓 JavaScript 直接調(diào)用,所以只能選擇以序列化字符串的形式通過通訊橋傳輸。而JSI做的事情就是將 C++ 中的常用類型(函數(shù)、對象等)一一映射到 JavaScript 中,我們就能在JS中直接調(diào)用C++的函數(shù)和對象了。因為消除了橋通訊帶來的序列化和異步調(diào)用的開銷,大大提升了線程通訊性能。
??
四、進(jìn)一步優(yōu)化的方向
1、減少UI層級
當(dāng)前基于多線程和JSI的架構(gòu)模式在鴻蒙系統(tǒng)的性能還算不差,但是在鴻蒙系統(tǒng)上同樣一個業(yè)務(wù)的UI層級是其他兩端層級的約2倍。原因在于在鴻蒙系統(tǒng)使用系統(tǒng)組件進(jìn)行遞歸渲染的時候,需要借助自定義組件進(jìn)行實現(xiàn),然而和iOS和Android端的命令式組件渲染不同,比如RomaDiv對應(yīng)iOS就是直接翻譯為UIView即可,在鴻蒙必須增加一個包裹的容器才是一個合法的自定義組件,比如Stack容器,這樣每個組件的層級就多了一層,層級過多會直接影響渲染性能,在一些復(fù)雜業(yè)務(wù)場景到達(dá)一定層級后會造成頁面掉幀和卡頓。
@Componentexport struct RomaDiv { build() { Stack(){ //借助wrapBuilder實現(xiàn)遞歸 ForEach(this.childrenTags, (childrenTag) => { RomaComponentFactory.builder()//RomaComponentFactory就是對應(yīng)鴻蒙系統(tǒng)提供的WrappedBuilder }) } } }
面對業(yè)界跨端框架面臨的這個共性問題,鴻蒙系統(tǒng)提供了C語言的命令式接口進(jìn)行組件創(chuàng)建,C組件接口是介于UI組件的Native實現(xiàn)和ArkTS對接層之間的一層C接口封裝,它繞過了狀態(tài)管理對組件變化、刷新的自動化管理,因此具有較好的性能。同時經(jīng)過初步驗證,接入C-API后,UI層級能直接和另外兩端保持一致,同時渲染性能也會得到大幅提升。
2、降低通訊成本
當(dāng)前JSI在鴻蒙系統(tǒng)的應(yīng)用中通過JSI打通C++,再通過NAPI從C++打通ArkTS,跨語言通訊成本高。如果接入了C-API,就避免了C++和ArkTS之間類型互相轉(zhuǎn)換和跨語言調(diào)用的開銷,也能帶來不少的性能提升。
3、JS邏輯下沉到C++
在當(dāng)前架構(gòu)中,JS線程運行著V-Dom樹創(chuàng)建、對比,樣式&屬性解析等一系列繁重的框架邏輯,如果我們能將這些JS代碼邏輯下沉到C++,框架邏輯運行效率會進(jìn)一步提升。
總結(jié)
本文講述了如何在鴻蒙系統(tǒng)中實現(xiàn)“動態(tài)化”跨端框架的高性能運行。包含探索方舟虛擬機(jī)運行方案時遇到的問題,以及基于V8虛擬機(jī)方案的具體提升手段和后續(xù)進(jìn)一步提升的方案。通過閱讀,你將能夠更好地理解和應(yīng)用這些技術(shù),提高跨端框架的性能,提升C端用戶體驗。
審核編輯 黃宇
-
C++
+關(guān)注
關(guān)注
22文章
2108瀏覽量
73618 -
虛擬機(jī)
+關(guān)注
關(guān)注
1文章
914瀏覽量
28160 -
鴻蒙系統(tǒng)
+關(guān)注
關(guān)注
183文章
2634瀏覽量
66302
發(fā)布評論請先 登錄
相關(guān)推薦
評論