Lynx(一個高效的跨平臺框架) 的 JS Binding 技術最主要的目的是搭建一個高效的與 JS 引擎解耦的通訊橋樑,同時具有 JS 引擎切換的能力。該技術經歷了屢次迭代,最終經過抽象的引擎接口層設計,在代碼層面作到對於 JS 引擎的解耦。目前 Lynx 在 Android 端支持 V8 和 JSC 引擎的切換。html
關於 JSC 和 V8 引擎的相關基礎知識能夠瀏覽上一篇文章android
Lynx 是一個 JS 驅動的跨平臺框架,提供了 JS 調用 Android 和 iOS 等平臺層的渲染能力,同時容許開發者拓展平臺能力,所以在 Lynx 中和 JS 通訊的除了核心 Runtime 層,還包括了處於平臺層的 Module 和 RenderObjectImpl,同時在框架中存在線程間通訊的狀況。結合上述 Lynx 框架的特性,在 JS Binding 迭代時遇到的主要的問題:ios
總體設計代碼在 runtime 目錄。c++
對於跨線程和跨平臺的參數轉化的問題,爲了便於參數在上下游的轉化(JS 與核心 C++ 層的轉化,核心層與平臺層 Android & iOS 的轉化),定義了 LynxValue 做爲通用傳遞參數,並根據不一樣平臺制定 LynxValue 的轉化規則,減小參數在跨層調用時繁瑣的轉化步驟。轉化規則如今包括 JSC 到核心層的 JSCHelper,V8 到核心層的 V8Helper,核心層到 iOS 層的 OCHelper,以及核心層到 Android 層的 JNIHelper。下面的圖能夠看出 LynxValue 流通與不一樣層次。git
主要數據結構github
在 JS 引擎代碼解耦方面,JSC 和 V8 在 JS 原型和 Extension 上的設計都是類似的邏輯,只是在實現的細節上不一致。如在 JSC 中利用 JSClassRef 描述原型上所具備的屬性和方法,同時能夠構造原型鏈,而 V8 中利用 FunctionTemplate 和 PrototypeTemplate 代替;JSC 中使用 JSObjectSetPrivate 接口爲 JS 對象綁定一個 C++ 對象,而 V8 則利用 ObjectTemplate::SetInternalField 方法代替。基於上述特色,Lynx 的 JSBinding 抽象了一層 JS 的原型構造器和方法鉤子的接口,以知足與 JS 的通訊功能。數組
JSVM 是表明 JS 運行的虛擬機,真正的實現文件交由各自引擎實現。安全
JSContext 爲 JS 引擎的控制上下文,同時是一個模板類,其中包含全局對象 Global,對於真正的 V8 和 JSC 的操做由其實現類 V8Context 和 JSCContext 決定。而與 JS 通訊主要使用對外接口 ClassTemplate 和內部接口 ObjectWrap。數據結構
ClassTemplate 用於構造 JS 原型的模板,經過該模板能夠註冊函數和變量鉤子等(Extension 功能。該對象持有PrototypeBuilder,PrototypeBuilder 由對應的 JS 引擎實現,用於構建 JSC 的 JSClassRef 或者是 V8 的 FunctionTemplate,同時能夠根據原型建立 JS 對象。 ClassTemplate 提供了宏定義幫助定義默認 ClassTemplate 的靜態方法,下面是宏定義的意義和用法:框架
DEFINE_CLASS_TEMPLATE_START
默認 ClassTemplate 構建的方法定義的開始REGISTER_PARENT
定義 ClassTemplate 的父親(原型鏈),在 START 和 END 之間使用。EXPOSE_CONSTRUCTOR
在 JS 上下文中暴露該 ClassTemplate 做爲構造器,在 START 和 END 之間使用。REGISTER_METHOD_CALLBACK
向 ClassTemplate 中註冊函數鉤子,在 START 和 END 之間使用。REGISTER_GET_CALLBACK
REGISTER_SET_CALLBACK
向 ClassTemplate 中註冊變量鉤子,在 START 和 END 之間使用。DEFINE_CLASS_TEMPLATE_END
默認 ClassTemplate 構建的方法定義的結束。DEFAULT_CLASS_TEMPLATE
獲取默認 ClassTemplate。defines.h 頭文件含有用於定義 JS 引擎鉤子函數的宏規則,C++ 類須要根據宏定義鉤子函數,並將函數指正註冊到 ClassTemplate 中,同時自身須要有對應的類方法(鉤子函數會進行回調)進行真正實現,才能完成原型的構建。ClassTemplate.h 中經過宏定義提供了快速構建一個與 C++ 對象默認的 ClassTemplate 對象。結合兩個宏定義規則,能夠實現快速構建與 JS 通訊的 C++ 類。下面是 defines.h 中宏定義的意義:
DEFINE_METHOD_CALLBACK
用於定義 JS 引擎函數鉤子,DEFINE_GROUP_METHOD_CALLBACK
用於定義帶方法名稱做爲參數的函數鉤子,METHOD_CALLBACK
用於獲取鉤子名稱DEFINE_SET_CALLBACK
DEFINE_GET_CALLBACK
用於定義 JS 引擎變量鉤子,SET_CALLBACK
GET_CALLBACK
用於獲取鉤子名稱。自定義類方法鉤子示例:
JS 變量的 Get 鉤子:base::ScopedPtr<LynxValue> Function();
JS 變量的 Set 鉤子: void Function(base::ScopedPtr<jscore::LynxValue>& value);
JS 方法鉤子:base::ScopedPtr<LynxValue> Function(base::ScopedPtr<LynxArray>& array);
ObjectWrap 用於創建 JS 對象和 C++ 對象(這裏指 LynxObject)的關係,即用於管理 C++ 對象生命週期,C++ 對象的生命週期是跟隨 JS 對象(固然 JS 對象只是對 C++ 對象進行引用計數上的增減,確保 C++ 對象在被其餘類引用時能夠被安全釋放或使用)。JS對象和C++對象綁定的時機在 ClassTemplate 建立 JS 對象時,這個時機由 JS 運行上下文決定(在 defines.h 中的鉤子函數中處理),無需開發者關心。
JS Binding 總體運行圖示,在 Lynx 開發中,JS 引擎的具體實現或者參數轉化規則對外無感知,利用 LynxObject 和 LynxValue 就能夠與 JS 通訊,完成 API 調用工做。LynxValue 和 JSValue 的轉化均是在 JSObject 和 LynxObject 相互調用時進行。
實例:定義與 JS 對象 console 關聯的 Console 類,實現 console.log 的函數調用,主要步驟以下
namespace jscore {
class Console : public LynxObject {
public:
Console(JSContext* context);
virtual ~Console();
// 定義 JS 引擎函數鉤子回調的類方法
base::ScopedPtr<LynxValue> Log(base::ScopedPtr<LynxArray>& array);
};
}
複製代碼
namespace jscore {
#define FOR_EACH_METHOD_BINDING(V) \ V(Console, Log)
// 定義須要進行 Extension 的函數鉤子
FOR_EACH_METHOD_BINDING(DEFINE_METHOD_CALLBACK)
// 定義默認的 ClassTemplate
DEFINE_CLASS_TEMPLATE_START(Console)
FOR_EACH_METHOD_BINDING(REGISTER_METHOD_CALLBACK)
DEFINE_CLASS_TEMPLATE_END
// 構造函數中傳入默認的 ClassTemplate
Console::Console(JSContext* context) : LynxObject(context, DEFAULT_CLASS_TEMPLATE(context)) {
}
Console::~Console() {}
base::ScopedPtr<LynxValue> Console::Log(base::ScopedPtr<LynxArray>& array) {
// Print log
return base::ScopedPtr<LynxValue>(NULL);
}
}
複製代碼
優勢:隔離 JS 引擎代碼,易於切換;無額外消耗的函數鉤子(通訊)實現,比 RN 的通訊更快;快速上手,相比於 Web IDL 沒有學習成本。
缺點:仍然須要在通訊類中手動編寫必定代碼;暫時只知足於和 JS 引擎通訊的功能,相比於 Web IDL 而言功能相對簡單,暫時無涉及多種外部語言。
Git 拉 Lynx 工程源碼,根據 How To Build 運行 Android 工程,在 Android 工程根目錄的 gradle.properties 中,經過設置 js_engine_type=v8/jsc
進行 V8 引擎和 JSC 引擎的切換。iOS 僅支持 JSC 引擎。
請持續關注 Lynx,一個高性能跨平臺開發框架。