用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解

WEEX依舊採取傳統的web開發技術棧進行開發,同時app在終端的運行體驗不輸native app。其同時解決了開發效率、發版速度以及用戶體驗三個核心問題。那麼WEEX是如何實現的?目前WEEX已經完全開源,並捐給Apache基金會,我們可以通過分析其源碼來一探究竟。

傳統的移動端開發,一個完整的業務需要維護三份終端代碼:Android、iOS、H5,這帶來了極大的開發成本以及維護成本。尤其是對處於業務初創期需要快速試錯的業務以及需要支持定期運營活動的業務。所以業界也一直在探索跨平臺方案,旨在通過一套代碼完成各個終端的業務邏輯。相關方案經過不斷演化,從早期的H5、Hybrid到如今的Cloud Native(雲原生),在開發效率和用戶體驗上都在一點點逼近最初的設想。

早期H5和Hybrid方案的核心是利用終端的內置瀏覽器(webview)功能,通過開發web應用滿足跨平臺需求。該方案可以解決跨平臺問題,同時可以提升發版效率。但其最大的弊端在於用戶體驗相較於native開發的app存在較大差距,經常出現頁面卡頓,加載慢等問題。

於是後來業界開始探索依舊利用web技術棧開發出媲美原生體驗app的方案,於是以WEEX為代表雲原生開發框架開始出現。所謂雲原生(Cloud Native)指可以通過雲端快速發佈(與遠程web應用發佈流程類似),同時還可以達到媲美原生App體驗的方案。WEEX依舊採取傳統的web開發技術棧進行開發,同時app在終端的運行體驗不輸native app。其同時解決了開發效率、發版速度以及用戶體驗三個核心問題。那麼WEEX是如何實現的?目前WEEX已經完全開源,並捐給Apache基金會,我們可以通過分析其源碼來一探究竟。

WEEX框架主要分為兩部分:

前端JavaScript框架

Native SDK

本文主要探討Native SDK的核心原理,其前端JavaScript框架會在後續的文章中進行介紹。

1 整體架構

首先來看下WEEX開發的整體架構:

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


從上圖中可以看到weex的大致工作流程:

研發人員利用web技術棧開發weex file,打包成JS Bundle,然後部署到服務器上

終端通過網絡獲取JS Bundle,然後在本地執行該JS Bundle

終端上提供了JS的執行引擎(JSCore)用於執行遠程加載到JS Bundle

JS執行引擎執行JS Bundle,並將相關渲染指令以及其他需要利用native能力的指令通過JS-Native Bridge透出

JS-Native Bridge將渲染指令分發到native(Andorid、iOS)渲染引擎,由native渲染引擎完成最終的頁面渲染

看完上述整體架構後,可以大致理解為何WEEX可以達到媲美原生的體驗,因為其頁面渲染並不是像H5方案一樣接入瀏覽器的渲染能力,而是原生渲染,所以本質上渲染出來的頁面就是一個native頁面。

接下來我們再來將端上的模塊進行詳細的拆分:

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


如上圖所示,WEEX NATIVE SDK大致可以分為如下幾個層級:

JS執行層:

JS執行引擎:JSCore,解釋並執行JS Bundle

main.js:提供WEEX runtime,SDK初始化,JS Core會首先加載main.js,為js bundle提供weex runtime

Bridge層:提供JS和Native的雙向通信能力

Dom層:維護頁面Dom結構

Render層:完成頁面渲染

native組件庫:本地UI組件庫,每一個組件對應一個html標籤,所以當我們在weex開發過程中使用到的各種標籤:div、text、image等等,最終都被轉化成為了一個native的控件

module manager、module庫:功能模塊管理層

WXSDKManger、WXSDKEngine:SDK全局環境維護

WXSDKInstance:weex 實例,一個js bundle對應一個weex實例

2 WEEX SDK初始化

有了上述大致架構和功能劃分後,我們以一個實際的例子來分析WEEX NATIVE SDK的運行邏輯。首先來看下WEEX SDK在初始化階段都做了哪些準備工作。

這裡以Andorid代碼為例進行分析:WEEX的初始化通常放在Application中,其初簡化的初始化邏輯入如下:

public class WXApplication extends Application {

@Override

public void onCreate() {

super.onCreate();

initWeex();

......

}

private void initWeex() {

// 自定義相關配置

InitConfig config=new InitConfig.Builder()

.setImgAdapter(new ImageAdapter()) // 自定義圖片適配器

.build();

WXSDKEngine.initialize(this,config);

// register module

try {

WXSDKEngine.registerModule("testmodule", TestModule.class); // 註冊自定義模塊

WXSDKEngine.registerModule("event", WXEventModule.class);

WXSDKEngine.registerComponent("richtext", RichText.class); // 註冊自定義UI組件

......

} catch (WXException e) {

e.printStackTrace();

}

}

}

從代碼中可以看到,weex的初始化比較簡單,主要完成兩件事:

完成初始化配置:比如指定相關適配器,比如圖片請求適配器

註冊自定義的UI組件和功能模塊

剩下的事情都交給WEEX SDK來完成了,那麼接下來就來看下WEEX SDK都做了些什麼?

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


具體代碼在WXSDKEngine.doInitInternal:

private static void doInitInternal(final Application application,final InitConfig config){

WXEnvironment.sApplication = application;

WXEnvironment.JsFrameworkInit = false;

WXBridgeManager.getInstance().post(new Runnable() {

@Override

public void run() {

long start = System.currentTimeMillis();

WXSDKManager sm = WXSDKManager.getInstance();

sm.onSDKEngineInitialize();

if(config != null ) {

sm.setInitConfig(config);

if(config.getDebugAdapter()!=null){

config.getDebugAdapter().initDebug(application);

}

}

WXSoInstallMgrSdk.init(application,

sm.getIWXSoLoaderAdapter(),

sm.getWXStatisticsListener());

boolean isSoInitSuccess = WXSoInstallMgrSdk.initSo(V8_SO_NAME, 1, config!=null?config.getUtAdapter():null);

if (!isSoInitSuccess) {

return;

}

sm.initScriptsFramework(config!=null?config.getFramework():null);

WXEnvironment.sSDKInitExecuteTime = System.currentTimeMillis() - start;

WXLogUtils.renderPerformanceLog("SDKInitExecuteTime", WXEnvironment.sSDKInitExecuteTime);

}

});

register();

}

這是WEEX SDK的初始化邏輯,其主要做了以下幾件事:

初始化WXBridge,同時啟動WXBridge線程,待接收指令。WXBridge在Android的實現本質上是一個基於HandlerThread的異步任務處理線程

initSo:加載so文件,即JS執行引擎

initScriptsFramework:加載SDK中的main.js,完成weex runtime的初始化

register:註冊SDK自帶的UI組件和功能模塊

3 頁面渲染

WEEX SDK在完成了初始化之後,即可開始渲染頁面了。接下來我們以如下這JS代碼為例,來介紹頁面的渲染邏輯:

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


JS代碼比較簡單,邏輯就不介紹了。接下來重點介紹,當終端獲取到如上圖右側的js bundle後,如何進行加載、渲染以及後續的相關邏輯執行。

3.1 weex實例創建

實際上當WEEX SDK獲取到JS Bundle後,第一時間並不是立馬渲染頁面,而是先創建WEEX的實例:

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


這幅時序圖中有兩個主要邏輯:

創建createInstance:創建一個weex實例,每一個JS bundle對應一個實例,同時每一個實例都有一個instance id。由於所有的js bundle都是放入到同一個JS執行引擎中執行,那麼當js執行引擎通過WXBridge將相關渲染指令傳出的時候,需要通過instance id才能知道該指定要傳遞給哪個weex實例

execJs:在創建實例完成後,接下來才是真正將js bundle交給js執行引擎執行

3.2 頁面渲染

在實例創建完成後,接下來就是頁面渲染了。首先來看下頁面渲染的整體流程:

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


js bundle涉及dom操作的執行都會被weex-vue-framework轉化成native dom api, 前端框架vue是基於virtual dom api,而weex的前端框架:weex-vue-framework的核心邏輯就是將vue的virtual-dom轉換成Native DOM API

weex終端的執行引擎在執行到Native DOM API後,則會將其轉化為Platform API,說白了就是通過WXBridge將Native DOM API以約定的方式轉發給native渲染引擎,完成頁面渲染

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


可以看到,在js執行引擎創建好weex實例後,會執行對應的JS Bundle,並在執行到platform api的時候將其通過wxbridge,發送給DomManager。相關代碼可參考:com.taobao.weex.bridge.WXBridge

3.2.1 createbody

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


一個頁面的DOM結構最外層是body,所以創建頁面一開始就是createbody,整個create body的過程大致可以分為以下幾個步驟:

WXBridge將create body指令發送給WXDom模塊。WXDom是另一個異步線程,負責維護頁面的Dom樹

WXDom創建一個新的dom樹,同時創建body節點

WXDom將create body指令傳遞給WXRenderManager渲染引擎,渲染引擎主要完成如下幾件事:

初始化一個組件實例,稱為mGodComponent

generateComponentTree:由於一個WEEX頁面就是由多個UI組件(Component)構成的一棵樹,所以渲染引擎會初始化組件樹

創建view

3.3.2 addElement

用WEB技術棧開發NATIVE應用(一):WEEX SDK原理詳解


創建完body後,需要在body中添加一個text組件,指向該操作的Native DOM API為addElement,其具體操作為:

WXDomManager:更新本地dom樹,添加text節點

WXRenderManager:本地渲染引擎添加相關組件:

從已註冊的組件中找到text對應的組件,並實例化

將初始化完成的text組件添加到body所對應的view之上

給text組件設定佈局、添加監聽事件

加入數據綁定

在此一個帶有一個text標籤的簡單頁面才算是渲染完成。值得一提的是,在WXRenderManager創建組件時,需要在本地已註冊的組件中需要標籤對應的組件,此處標籤對應的組件為com.taobao.weex.ui.component.WXText,其本質上是一個TextView。從這裡可以發現,其實我們在JS Bundle中指定的各種標籤,其實都最終被轉化為了一個native的控件。這也就是為什麼用WEEX開發出來的app,本質上還是一個Native App。

其他的對應關係還有:

div 對應WXDiv

image 對應WXImage

list對應WXListComponent

a對應WXA

……

4 總結

通過前文的介紹,相信大家對WEEX有了一個初步的系統認識。簡單來說,WEEX放棄了傳統的Webview,而是搭建了一個native化的瀏覽器,因為用native的方式實現了一個瀏覽器的大部分核心組成成分:

JS 執行引擎

渲染引擎

DOM樹管理

網絡請求,持久層存儲等等能力

另外為了保證整個SDK的運行效率,SDK維護了三個線程:

bridge線程:完成js到native之間的通信

dom線程:完成dom結構的構建

渲染線程:完成UI渲染,也就是UI線程

以上就是WEEX SDK的大致框架和核心邏輯,篇幅有限,無法面面俱到


分享到:


相關文章: