Node和Electron環(huán)境下集成第三方原生SDK

NodeJs Addons是為了能讓nodejs調(diào)用原生模塊而設(shè)計的機制众眨,之前對一些常見原生模塊的重編譯做過梳理慧瘤。但那都是Git上已經(jīng)封裝好的nodejs Addons庫俺泣, 但若項目需要集成第三方提供的原生SDK距潘,如何使用Nodejs去調(diào)用蜓氨,以及如何打包到Electron項目中?

背景

項目中需要集成第三方投屏功能南蓬,合作廠商扔過來Mac和Windows SDK纺非。文檔看了后哑了,接口確實設(shè)計的比較簡單易用,但SDK是為原生框架設(shè)計的烧颖,沒有現(xiàn)成的Node集成方案弱左。

Mac SDK是Framework形式,Mac開發(fā)基于OC炕淮,所以我們選用 NodObjC 嘗試調(diào)用SDK拆火。NodObjC能讓Node直接調(diào)用Framework暴露的接口。其本身也基于ffiref這兩個庫涂圆。

Windows SDK比較復(fù)雜们镜,SDK申明的接口全放置在一個頭文件中,在常規(guī)VC項目里润歉,我們只需引入頭文件和核心.lib文件模狭,即可實現(xiàn)調(diào)用。這里踩衩,我們看了接口封裝形式嚼鹉,感覺不適合使用ffi這類JS庫,決定自己寫一個Addons來實現(xiàn)調(diào)用驱富。

Mac

環(huán)境

下載NodObjc锚赤,npm install nodobjc

NodObjc依賴的ffiref兩個庫屬于Addons,需要根據(jù)運行環(huán)境去重編譯褐鸥。需要先全局安裝好node-gyp线脚,然后根據(jù)當(dāng)前是在node環(huán)境還是Electron環(huán)境調(diào)試去重編譯。

這里遇到一個Node版本導(dǎo)致的問題叫榕,nodobjc在依賴庫的版本號上似乎有些問題浑侥,導(dǎo)致依賴ref無法在node 10下編譯成功。通過n安裝node 8翠霍,重新下載和編譯锭吨。

API

  • 引用

    const $ = require('nodobjc');
    
    $.import(frameworkPath);
    $.framework('Foundation');
    
  • JS與OC的類型轉(zhuǎn)換

    字符串: String -> NSString,var str = $('abc');

    Number類型: Number -> NSNumber寒匙, var num = 123;

    Boolean: bool -> Bool零如,var isTrue = $.Yes;

    回調(diào)函數(shù): callback -> Block:

    OC接口:

    [[Test commonTest] startTest:@"123456" completeBlock:^(BOOL succeed, NSError *error) {
    
    }];
    

    通過NodObjc調(diào)用該OC接口:

    const startTest = (data) => {
      const param = $(data);
      const completeBlock = $(function(self, success, err) {
        console.log('test result', success, err);
      }, ['v', ['?', 'B', '@']]);
      commonTest('startTest', param, 'completeBlock', completeBlock);
    }
    

    NodObjc的文檔并不是很詳細,需要一定的OC語法基礎(chǔ)锄弱。當(dāng)然考蕾,花點時間看下NodObjc的源碼也能知道具體調(diào)用方式。

  • 編譯與打包

    打包有兩個注意點会宪,一是需要通過node-gyp將ffi和ref庫重編譯生成Electron環(huán)境下可用的.node肖卧。二是適應(yīng)electron-builder的打包規(guī)則。

    有問題的打包方案:

    1. 直接打包到app.asar掸鹅,打完包會發(fā)現(xiàn)js無法引用Framework

    2. 通過配置electron-builder打包規(guī)則塞帐,將Framework所有文件打包到app.asar.unpack目錄拦赠。講道理,按之前的經(jīng)驗葵姥,這樣應(yīng)該就可以了荷鼠。結(jié)果直接在打包的簽名這步GG了±菩遥看上去electron-builder無法對二進制文件進去簽名:

      ***.framework, bundle format unrecognized, invalid, or unsuitable
      

    解決方案

    最后允乐,選擇用extraResources字段,將Framework從打包文件中抽出來削咆,直接復(fù)制到Mac應(yīng)用的Resources目錄下牍疏,然后在調(diào)用的js文件中,根據(jù)運行環(huán)境動態(tài)選擇調(diào)用路徑:

    const path = require('path');
    
    const isDev = process.env.NODE_ENV == 'dev';
    
    const devPath = path.resolve(__dirname, 'Test.framework');
    const prodPath = path.resolve(__dirname, 'Test.framework').replace('app.asar/src', 'src');
    // const frameworkPath = path.resolve(__dirname, 'HPOfficeCastWork.framework');
    const path = isDev ? devPath : prodPath;
    const frameworkPath = require(path);
    

Windows

windows方面拨齐,我們先自己按照Node Addons的開發(fā)規(guī)則來實現(xiàn)一個.node文件鳞陨,后面直接調(diào)用.node來實現(xiàn)功能。

環(huán)境

首先配好windows的node-gyp編譯環(huán)境瞻惋。先下載安裝python2.7和windows-build-tools炊邦。將python路徑配置到系統(tǒng)環(huán)境。

因為要編譯C++程序熟史,需要保證系統(tǒng)已安裝好C++相關(guān)的組件。C++組件缺失會在Addons模塊編譯的時候報錯窄俏,根據(jù)具體錯誤內(nèi)容蹂匹,下載對應(yīng)缺失組件即可。

在node環(huán)境下寫模塊demo的時候凹蜈,編譯報錯:無法解析外部符號限寞。這個錯一開始以為是某一塊的語法有問題,其實是因為SDK提供的lib是32位仰坦,而我們編譯的node環(huán)境是64位履植。重新安裝和配置32位的Node環(huán)境即能解決這個問題。

binding.gyp

node-gyp通過binding.gyp文件配置模塊的編譯悄晃,因此玫霎,先了解好.gyp的屬性很有必要:gyp3.org

{
    "targets": [
    "target_name": "test",
    "sources": ["test.cc"],
    "include_dirs": [
      "inc",
      "<!(node -e \"require('nan')\")",
    ],
    "libraries": [
      "../lib/sdk.lib"
    ],
    "conditions": [
        [
        "OS='win'", {
          "copies": [
            {
              "destination": "<(PRODUCT_DIR)",
              "files": [
                "<(DLL_ROOT)/dnssd.dll",
                "<(DLL_ROOT)/avutil.dll",
                ...
              ]
            }
          ]
        }
      ]
    ]
  ]
}

結(jié)合實際開發(fā),介紹幾個常用字段:

include_dirs: 要用到的頭文件所在目錄妈橄,<!(node -e \"require('nan')\")用于引入nan的頭文件庶近,<!是命令行擴展,gyp會將后面的字符通過shell執(zhí)行眷蚓。

conditions鼻种,自然是判斷條件,通常我們通過OS字段來判斷當(dāng)前的操作系統(tǒng)環(huán)境沙热,對應(yīng)的值是win叉钥,maclinux罢缸。

copies是為了執(zhí)行文件的拷貝。起初投队,沒有加這段配置枫疆,編譯成功后,我們引用生成的.node會報the specified module could not be found錯誤蛾洛。這里可以通過工具dependency walker分析.node文件养铸,查看缺失的依賴。一般這種情況轧膘,將相關(guān)dll文件放到.node同級目錄即可钞螟。 于是,通過copies可以在編譯后將指定文件復(fù)制到目標(biāo)目錄谎碍。<(PRODUCT_DIR)即表示.node生成后的目錄鳞滨。

Addons開發(fā)

Addonss是Node提供的動態(tài)鏈接共享對象,具有C/C++類庫的調(diào)用能力蟆淀。bingding.gyp中拯啦,我們配置了sources字段的值test.cc。在test.cc中熔任,我們通過引入v8.h, node.h , SDK提供的頭文件等來實現(xiàn)對C++接口的調(diào)用褒链。

#include <node.h>
#include <nan.h>
#include "inc/test.h"

namespace test
{

    using namespace v8;
    using namespace test; 
  
  static IMirror *pobMirror = 0;
  
  void initSdk(const FunctionCallbackInfo<Value>& args)
  {
    Isolate* isolate = args.GetIsolate();

    std::string appKey = *Nan::Utf8String(args[0]);
    std::string pinCode = *Nan::Utf8String(args[1]);
    std::string userId = *Nan::Utf8String(args[2]);
    std::string serverAddr = *Nan::Utf8String(args[3]);
    unsigned int serverPort = args[4]->Uint32Value();
    bool isEnterprise = args[5]->BooleanValue();
    Local<Function> cb = Local<Function>::Cast(args[6]);
    
    emRtn = pobMirror->Start(appKey, pinCode, userId, serverAddr, 
      serverPort, isEnterprise);
    
    const unsigned argc = 1;
    Local<Value> argv[argc] = {String::NewFromUtf8(isolate, ToString(emRtn))};
    cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);
    }

  
    void Initialize(Local<Object> exports)
    {
    NODE_SET_METHOD(exports, "initSdk", InitSdk);
    }

    NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
} // namespace hpcast

js調(diào)用Addons的接口,傳遞進來的是v8數(shù)據(jù)類型疑苔,而我們調(diào)用SDK接口甫匹,需要將其轉(zhuǎn)為C++數(shù)據(jù)類型。這里引用了Nan的類型轉(zhuǎn)換方法:

std::string serverAddr = *Nan::Utf8String(args[3]);
unsigned int serverPort = args[4]->Uint32Value();
bool isEnterprise = args[5]->BooleanValue();

對于回調(diào)函數(shù)惦费,在轉(zhuǎn)為Local<Function>類型后兵迅,通過Call方法觸發(fā)回調(diào):

Local<Function> cb = Local<Function>::Cast(args[6]);

const unsigned argc = 1;
Local<Value> argv[argc] = {String::NewFromUtf8(isolate, ToString(emRtn))};
cb->Call(isolate->GetCurrentContext()->Global(), argc, argv);

打包

和mac類似,如果直接將SDK和源碼一起打包薪贫,electron是無法引入SDK的恍箭。因windows暫時沒有簽名,我們直接通過asarUnpack將SDK文件打包到app.asar.unpack目錄瞧省。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末扯夭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子鞍匾,更是在濱河造成了極大的恐慌勉抓,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件候学,死亡現(xiàn)場離奇詭異藕筋,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門隐圾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伍掀,“玉大人,你說我怎么就攤上這事暇藏∶垠裕” “怎么了?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵盐碱,是天一觀的道長把兔。 經(jīng)常有香客問我,道長瓮顽,這世上最難降的妖魔是什么县好? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮暖混,結(jié)果婚禮上缕贡,老公的妹妹穿的比我還像新娘。我一直安慰自己拣播,他們只是感情好晾咪,可當(dāng)我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著贮配,像睡著了一般谍倦。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上泪勒,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天剂跟,我揣著相機與錄音,去河邊找鬼酣藻。 笑死,一個胖子當(dāng)著我的面吹牛鳍置,可吹牛的內(nèi)容都是我干的辽剧。 我是一名探鬼主播,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼税产,長吁一口氣:“原來是場噩夢啊……” “哼怕轿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辟拷,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤撞羽,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后衫冻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體诀紊,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年隅俘,在試婚紗的時候發(fā)現(xiàn)自己被綠了邻奠。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笤喳。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖碌宴,靈堂內(nèi)的尸體忽然破棺而出杀狡,到底是詐尸還是另有隱情,我是刑警寧澤贰镣,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布呜象,位于F島的核電站,受9級特大地震影響碑隆,放射性物質(zhì)發(fā)生泄漏恭陡。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一干跛、第九天 我趴在偏房一處隱蔽的房頂上張望子姜。 院中可真熱鬧,春花似錦楼入、人聲如沸哥捕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽遥赚。三九已至,卻和暖如春阐肤,著一層夾襖步出監(jiān)牢的瞬間凫佛,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工孕惜, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留愧薛,地道東北人。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓衫画,卻偏偏與公主長得像毫炉,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子削罩,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,916評論 2 344