進擊ReactNative-疾如風

閱讀原文
進擊ReactNative疾如風
目前沒有找到讓我能達到Native開發熟練度的ReactNative文章,在@胡朝旭大神點撥“ReactNative涉及Native(Android/iOS)、C和JavaScript,業界大部分只會其一,少數會其二,全會的不是一般的人,而且他們也沒有時間寫這個”下,
我的機會來了
ReactNative涉及技術棧包含前端、客戶端、跨平臺通信,語言包含Java/Object-C、C、JavaScript。
直接看源碼肯定是一頭霧水【大神當我這句話沒說】,
我嘗試先從“原理+實踐,現學現做”的角度手寫ReactNative,加深理解。
目的是先學會怎麼用,再去想爲什麼!

情境(Situation)

約法三章

  1. 本文還沒有觸及到ReactNative源碼,屬於我單方面的臆想,中間雜糅一些段子和囉嗦的三觀,純粹爲博一笑。
  2. 本文是在AdvanceOnReactNative Demo運行通過(環境爲Mac電腦)的基礎上寫作,Native端從Java出發,目前不包含Object-C【因爲我是Android開發,不會iOS】。
  3. 本文是我解決問題的一種思路,會因爲我目前水平侷限,如果有其他更優解,望大神不吝賜教。

三問

  1. 我是誰:專注於移動互聯網的Android野生程序猿。
  2. 從哪來:隸屬於大前端(FontEnd、iOS、Android)戰鬥序列,和全棧小夥伴們(產品、研發(前端、後端)、測試)並肩作戰。
  3. 到哪去:全棧工程師。

三問
不管是誰

三看

向後看

  1. 互聯網時代,瀏覽器一統天下,大部分需求(聊天、購物、看劇、查資料等)用瀏覽器“即開即用”。只有少量工具級軟件使用Native(Word、PPT、Excel、翻譯詞典等)開發。
  2. 移動互聯網時代,大部分需求有獨立應用(美團、手機百度、天貓、京東、頭條等),但是一定也有匹配的網頁版,否則你無法用微信分享出去,而且用戶手機一般會預裝上面的巨無霸應用,基本涵蓋吃喝玩樂,所以不大可能再安裝其他同質應用,實在不行先在網頁版裏面體驗一下。
  3. 下載安裝一個應用弊端:下載花時間、浪費流量、佔空間、影響手機性能、泄露隱私等。
  4. 用戶是上帝,你能讓上帝乖乖聽你的麼?

向外看

  1. 業界領袖Facebook 15年推出ReactNative、目前已席捲全球,蒸蒸日上;Google 17年推出Flutter,炙手可熱。國內巨擘微信小程序、阿里Weex、大衆點評Picasso等。無一不是時代洪流的弄潮兒。
  2. 客戶端動態化方案技術演進:Web➢Hybrid➢插件化➢熱更新➢ReactNative、Weex、小程序、Fultter。
  3. 公司前端人數近年來快速增長,過去App中應用主要是Native實現(H5、Android、iOS三足鼎立),現在大部分被H5取代(H5和Android&iOS平分秋色),特別是創新性需求,H5快速試錯、隨時上線造就其終端王者地位。
  4. 移動互聯網瞬息萬變(H5搶佔一波又一波風口,根本不用等Native後援),手機硬件性能提升、通信網絡不斷升級、各種前端方案百花齊放(離線化、AMP( Accelerated Mobile Pages ,移動頁面加速)PWA(Progressive Web App,漸進式WEB應用)),爲前端一統天下提供了物質基礎。

向前看

  1. “科學技術是第一生產力。”– 鄧爺爺
  2. “勞動生產力是隨着科學和技術的不斷進步而不斷髮展的。” – 馬克思
  3. 總之,動態化方案的出現,不是爲了替代誰,更多是爲了給用戶更好的體驗,同時讓業務可以更快的迭代,並在不斷的嘗試中,給用戶帶來更好的產品。– 再談移動端動態化方案
  4. “時勢不可盡倚,貧窮不可盡欺,世事翻來覆去,須當周而復始。”– 寒窯賦




△ – 葉俊星 美團旅行前端技術體系的思考與實踐

三思

思危

  1. 前端工程師利用ReactNative、Fultter等技術開發應用,一個(前端工程師)頂兩(Android和iOS工程師)。
  2. 公司不會花錢養閒人(一個前端工程師就能完成的工作,幹嘛要僱兩個客戶端工程師)。
  3. JavaScript大行其道,一種語言,能跨端開發(前端瀏覽器應用和後端NodeJS服務)。前端工程師快速向全棧工程師演進。
  4. 充分挖掘客戶端計算力,對公司運營成本是巨大誘惑(想一想1億個手機的計算都在服務端,要多少服務端機器,還要解決各種網絡傳輸和負載均衡問題,這個錢能省幹嘛不省)。

思退

  1. 開放心態,擁抱變化。侷限於客戶端開發,下崗是早晚的事,除非你足夠牛逼。
  2. 知己知彼,是時候該停下客戶端腳步,去了解前端技術棧了。

思變

  1. 這是個客戶端向大前端轉型的機會,是邁向全棧工程師的起點。
  2. 客戶端工程師轉大前端工程師是富有挑戰性的工作,但好處不言而喻。
  3. 客戶端較前端有無法逾越的性能優勢,單純的前端開發無法發揮極致的用戶體驗,所以,天下終究是全棧工程師(客戶端、前端、服務端)的天下。相較於前端工程師學習客戶端技術,客戶端工程師學前端門檻會低很多。
  4. 技術殊途同歸,原理算法如出一轍,技術始終是工程師的核心競爭力之一。 – 我的導師@喬璞大神
  5. “天下武功,無招不破,唯快不破。”– 星爺的《功夫》
  6. “假如禍事不可免的話,朕情願它早點來。”– 《康熙王朝》中康熙回答周培公“萬一吳三桂造反,皇帝的心思是什麼”




△ – 《大明王朝1566》

三願

  1. 尋門而入:ReactNative道理我都懂,但是仍無從下手?
  2. 閉門合轍:手工自作簡易版ReactNative,深入分析工業機器ReactNative實現原理(驅動Virtual DOM和Native通信原理),ReactNative文章、高階組件。
  3. 破門而出:ReactNative是前端的一條溝,掉進去了,叫挫折,爬出來了,叫成長。

衝突(Complication)

  1. 我是專注於移動互聯網的北漂打工仔,前端的活我看到,但沒有學過,不會做,侷促於Android客戶端開發。
  2. 我團大面積鋪開ReactNative,作爲老油條,我不入地獄誰入地獄。如其束手就擒,不如奮起一搏。
  3. 短兵相接半月左右,一度憑藉自己多年累積的方法論和人生觀,直面殺入ReactNative源碼,硬着頭皮看了3遍大牛郭孝星的ReactNative源碼篇,JavaScript、C++、Android來回切換,各種高能預警,磕得我一臉狗血,整得我暈頭轉向、稀裏糊塗,最終身體不適,差點撲街。
  4. 直接debug源碼,JavaScript層代碼沒法看,一個套着一個,看了半天都沒有找到門在哪裏;Android層算找到了消息通信的往來入口。
  5. 卒子的命運,註定是身不由己。“今亡亦死,舉大計亦死,等死,死國可乎?”– 《陳涉世家》
  6. 最樸實的方法,敢於直麪人生,我開始努力切入ReactNative源碼了,目標是達到客戶端熟練度。


理想VS現實

疑問(Question)

5W2H分析法

  1. What:目標是什麼,核心問題是什麼?
  2. Why:爲什麼要這麼做,業界的現狀是什麼?
  3. Who:都有哪些角色,誰是甲方,誰是乙方,受益者是誰?
  4. When:什麼時候的事,什麼時機切入?
  5. Where:場景是什麼?
  6. How:怎麼做到的(實現原理)?
  7. How Much:有什麼價值,投入產出比是多少,怎麼衡量?


5W2H

靈魂拷問三連

答案(Answer)

三原則

“有理、有利、有節”是毛澤東在《目前抗日統一戰線中的策略問題》中提出的對敵戰爭“三原則”

有理

  1. 價值:不忘初心,打破客戶端工程師邊界,超越自我,強化核心競爭力。
    開拓萬里之波濤,布國威於四方。”–明治天皇
  2. 目標:以ReactNative作爲演進全棧工程師的契機,深入理解背後的原理,擴大自我生存空間。
    “設計不僅僅是外觀感受。設計關乎於工作原理。”–史蒂夫·喬幫主
  3. 問題
    • 成本大:雙端(Android和iOS)獨立開發一份,關鍵是功能還一樣,能不能一個人幹兩個人的活?
    • 週期長:依賴發版(一般一個月一版,跟不上移動互聯網的風口),需要用戶升級(沒事瞎升級,整一堆根本用不上的亂七八糟功能,除了浪費我流量,就是讓手機更卡了)。
    • 代價高:熱修復不成熟,難以及時止損,好羨慕後臺和前端就可以“偷偷地、悄悄地”把bug改了,客戶端即使發現了問題,等到發版修復,再小的問題也鬧得滿城風雨了。
      “且欲防微杜漸,憂在未萌。”– 宋書·吳喜傳
    • 體重胖:各家應用的佈局都是構建生態平臺,打造巨無霸應用,實現一桶天下。隨着業務迭代和時間這把殺豬刀,應用體重無可奈何的飆升。讓我減肥?開玩笑!你知道我爲這身材花了多少錢嗎?


站在巨人的肩膀上

  1. 途徑:MECE分析法(Mutually Exclusive Collectively Exhaustive,“相互獨立,完全窮盡”)。窮舉所有的可能性,比方我拿起殺豬刀,假裝庖丁解牛,手工製作一個簡易版ReactNative。


MECE

有利

升職加薪走上人生巔峯

  1. 時間:洞悉ReactNative底層原理,有助於更快更好地完成工作,節約時間。是不是面對ReactNative紅屏不知所措?遇到問題沒法深入,不得不膚淺的換一種解決方案試試?網上資料太少,知其然不知其所以然,沒法隨心所欲、遊刃有餘?
  2. 加薪:一分耕耘一分收穫。
  3. 升職:公司不會虧待你的。
  4. 信心:看,XXX哥果然不是蓋的;我感覺我能搞定XXX。
  5. 榮譽:🐂🐝、💯、👏、🌹、🎖。

有節

  1. 站在巨人的肩膀上,不要重複造輪子。
    站在巨人的肩膀上
  2. 戰線不要太長,明天的你很輕鬆做到的,現在做可能有點吃力,畢竟我們還在成長,還有明天。
  3. 把握分寸,畢竟精力有限。
    把握分寸

庖丁解牛

磨刀霍霍

  1. 模仿最小可用產品(Minimum Viable Product, MVP)理念,手工製作一個對角棋AI程序,JSX控制AI算法和UI,Java控制檯輸出棋譜和對弈交互。
    “世事如棋盤,世人若棋子。這年頭,誰利用誰還不一定!”– 《畫江湖》李克用 
    最小可用產品(Minimum Viable Product, MVP)

一刀兩斷

  1. 軟件 = 邏輯(邏輯處理引擎,如JavaScriptCore) + 界面(渲染引擎,如WebCore) 。界面是靜態圖片(像連環畫),通過邏輯操作界面元素動態放映(像動畫片)。
  2. 邏輯處理引擎跟進用戶輸入進行邏輯處理,輸出界面描述文本給渲染引擎。
  3. 渲染引擎解析界面描述文本(Html、JSX、佈局代碼等)和圖片資源,調用系統圖形API(OpenGL等),刷新界面。

吞刀吐火

  1. ReactNative中JavaScript代碼處理邏輯,生成界面描述文本給到Native,Native根據界面描述文本協議,解析成界面對象,調用圖形API刷新界面。
  2. Java如何運行JavaScript?

Java和JavaScript直接通信

上網搜索個Demo,拿來主義
{:.info}

  1. Java可以通過ScriptEngine直接調用知悉JavaScript代碼。 
  2. 參考java與js通信java ScriptEngine 使用實現Java運行JS代碼HelloScriptEngine.java
  3. 核心代碼示例如下(完整代碼詳見HelloScriptEngine.java

    public void testJSCore2() {
    // 初始化JS腳本引擎
    ScriptEngineManager factory = new ScriptEngineManager();
    ScriptEngine engine = factory.getEngineByName("JavaScript");
    // 注入全局變量,a = 4, b = 6
    engine.put("a", 4);
    engine.put("b", 6);
    try {
        // 運行JS代碼字符串
        // 定義求最大值函數max_num
        // 執行max_num(a,b),a 和 b對應上面設置的全局變量
        Object maxNum = engine.eval("function max_num(a,b){return (a>b)?a:b;}max_num(a,b);");
        System.out.println("max_num:" + maxNum);
    }
    catch (Exception e) {
        e.printStackTrace();
    }
    }
    // 運行命令行:
    shengshuqiangdeMacBook-Pro:java shengshuqiang$ javac HelloScriptEngine.java 
    shengshuqiangdeMacBook-Pro:java shengshuqiang$ java HelloScriptEngine
    hello,SSU
    2
    ./hello.js
    hello js
    19
    handleMsg:我服SSU,一個牛逼💯的人
    我服SSU,一個牛逼💯的人
    max_num:6

    本來想簡化點複雜度,避開C++這一層。
    但是無法做到雙端通信和共享上下文,無奈ㄟ( ▔, ▔ )ㄏ此路不通,只能回到起點 。
    躲不了,Just Do C++ !

Java和C++通信

JNI(JAVA Native Interface):Java本地接口,它是一種協議,提供一套編程框架,讓Java和本地語言(C/C++)能相互調用。

  1. 通過JNI初探,跑通第一個Java和C++調用示例HelloJNI.java。
  2. 核心代碼示例如下(完整代碼詳見HelloJNI.java

    // HelloJNI.java
    public class HelloJNI {
    static {
        System.loadLibrary("NativeLibrary");
    }
    public static native void sendMsg(String msg);
    public static void main(String[] args) {
        System.out.println("Hello SSU!");
        sendMsg("SSU");
    }
    }
    // HelloJNI.cpp
    JNIEXPORT void JNICALL Java_HelloJNI_sendMsg(JNIEnv * env, jclass cls, jstring jstr) {
    const char* str = env->GetStringUTFChars(jstr, NULL);
    printf("Java_HelloJNI_sendMsg:%s\n", str);
    env->ReleaseStringUTFChars(jstr, str);
    }
    // 運行命令行:
    shengshuqiangdeMacBook-Pro:java shengshuqiang$ javac HelloJNI.java
    shengshuqiangdeMacBook-Pro:java shengshuqiang$ javah HelloJNI
    shengshuqiangdeMacBook-Pro:java shengshuqiang$ gcc -dynamiclib HelloJNI.cpp -o libNativeLibrary.jnilib -I ./include/
    shengshuqiangdeMacBook-Pro:java shengshuqiang$ java HelloJNI
    Hello SSU!
    Java_HelloJNI_sendMsg:SSU

JNI進階

  1. JNI規範
  2. JNI規範配套代碼示例JNI Example

JNI允許JAVA和本地代碼間的雙向交互
△ – 農場老馬JNI規範

JavaScript和C++通信

JS引擎有JSCore和V8,我選擇V8

  1. V8源碼,當然,我是看不懂的。
  2. 先找幾篇文章學習一下,知其然
    1. 使用 Google V8 引擎開發可定製的應用程序
    2. 認識 V8 引擎
    3. 淺讀V8——強大的JavaScript引擎
    4. V8引擎在C++程序中使用簡介
  3. 這裏,我掉到一個坑裏面了,就是自己嘗試編譯V8源碼,發現gclient根本下載不下來,一度淚奔┭┮﹏┭┮
  4. 糾纏了一段時間後,我抖了個小機靈,github上面搜索可運行的V8Demo代碼。
  5. 人間只有真情在,還真給我找到一個可用的 依賴V8動態庫運行JS代碼coderzh/v8-demo,裏面是直接引用編譯好的v8動態鏈接庫libv8.dylib、libv8_libplatform.dylib。
  6. 開啓了我盲人摸象的第一步。裏面用到了CMake和Make編譯運行代碼,我又是第一次用到。
// main.cc
{
    // 初始化上下文
    v8::Isolate::Scope isolate_scope(isolate);
    v8::HandleScope handle_scope(isolate);
    v8::Local<v8::Context> context = v8::Context::New(isolate);
    v8::Context::Scope context_scope(context);

    // 傳入簡單JS字符串拼接代碼"Hello, World!"
    v8::Local<v8::String> source = v8::String::NewFromUtf8(isolate, "'Hello' + ', World!'", v8::NewStringType::kNormal).ToLocalChecked();

    // Compile the source code.
    v8::Local<v8::Script> script = v8::Script::Compile(context, source).ToLocalChecked();

    // Run the script to get the result.
    v8::Local<v8::Value> result = script->Run(context).ToLocalChecked();

    // Convert the result to an UTF8 string and print it.
    v8::String::Utf8Value utf8(isolate, result);
    printf("%s\n", *utf8);
}
// 命令行
// 直接運行會有個小問題,運行會報錯“dyld: Library not loaded: /usr/local/lib/libv8.dylib”,找不到v8動態鏈接庫
// 解決方案是把libs下動態鏈接庫文件直接複製到/usr/local/lib即可(cp ../libs/* /usr/local/lib) 。
shengshuqiangdeMacBook-Pro:AdvanceOnReactNative shengshuqiang$ mkdir build
shengshuqiangdeMacBook-Pro:AdvanceOnReactNative shengshuqiang$ cd build/
shengshuqiangdeMacBook-Pro:build shengshuqiang$ cmake ..
-- The C compiler identification is AppleClang 10.0.0.10001145
-- The CXX compiler identification is AppleClang 10.0.0.10001145
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/build
shengshuqiangdeMacBook-Pro:build shengshuqiang$ make
Scanning dependencies of target v8_demo
[ 50%] Building CXX object CMakeFiles/v8_demo.dir/src/main.cc.o
[100%] Linking CXX executable v8_demo
[100%] Built target v8_demo
shengshuqiangdeMacBook-Pro:build shengshuqiang$ ./v8_demo
dyld: Library not loaded: /usr/local/lib/libv8.dylib
  Referenced from: /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/build/./v8_demo
  Reason: image not found
Abort trap: 6
shengshuqiangdeMacBook-Pro:build shengshuqiang$ cp ../libs/* /usr/local/lib
shengshuqiangdeMacBook-Pro:build shengshuqiang$ ./v8_demo
我服SSU,一個牛逼💯的人

v8-say-hello

  1. coderzh/v8-demo只能簡單運行JS字符串拼接代碼,雖然看到了曙光,但距離旭日還遙不可及。
  2. 再一次被命運女神眷顧,嵌入V8的核心概念嵌入V8的核心概念1助我跑通了v8源碼中有三個例子(hello-world.cc、process.cc、shell.cc),這下前進了關鍵性的一大步,這是一個里程碑。
    1. hello-world.cc:簡單打印出“hello world”。
    2. process.cc:演示C++如何調用JavaScript函數。
    3. shell.cc:演示如何暴露native function到JavaScript。
// hello.js
// 對角棋程序
// 初始化,主要配置棋局輸出字符
init = () => {
    RedArmy = '🔥'
    BlackArmy = '💧'
    EmptyArmy = '🎄'
    Board = [EmptyArmy, BlackArmy, BlackArmy, BlackArmy, EmptyArmy, EmptyArmy, EmptyArmy, RedArmy, RedArmy, RedArmy]
    Location = ['⑩','①','②','③','④','⑤','⑥','⑦','⑧','⑨']
    Left2Right = '一'
    Top2Bottom = '|'
    LeftBottom2RightTop = '╱'
    LeftTop2RightBottom = '╲'
    Space = '\t'
    stepIndex = 0

    printBoard()
    printMsg("請🌴🐑");
}

// 構建棋子字符
buildChess = index => (Location[index] + Board[index])

// 打印棋盤字符
printBoard = () => {
    let str = buildChess(1) + Space + Left2Right + Space + buildChess(2) + Space + Left2Right + Space + buildChess(3) + '\n'
    + Top2Bottom + Space + LeftTop2RightBottom + Space + Top2Bottom + Space + LeftBottom2RightTop  + Space + Top2Bottom + '\n'
    + buildChess(4) + Space+ Left2Right + Space + buildChess(5) + Space + Left2Right + Space + buildChess(6) + '\n'
    + Top2Bottom + Space + LeftBottom2RightTop + Space + Top2Bottom + Space + LeftTop2RightBottom  + Space + Top2Bottom + '\n'
    + buildChess(7) + Space + Left2Right + Space + buildChess(8) + Space + Left2Right + Space + buildChess(9) + '\n'
    printMsg(str)
}

// 打印接口,因爲console.log在V8中無法直接使用,這裏調用C++打印函數print
printMsg = msg => {
    // console.log(msg)
    print(msg)
}

// 接收輸入下棋指令
// 指令字符串爲兩位整數,第一位表示源位置,第二位表示目標位置
receiveOrderStr = orderStr => {
    if (orderStr && orderStr.length == 2) {
        const srcIndex = parseInt(orderStr.charAt(0))
        const destIndex = parseInt(orderStr.charAt(1))
        // 輸入指令有效性判斷
        if (srcIndex >= 1 && srcIndex <= 9 && destIndex >= 1 && destIndex <= 9) {
            handleOrder(srcIndex, destIndex)
        } else {
            printMsg('🈲🚫犯規⛔️輸入指令【'+ orderStr + '】錯誤,應該爲兩位1-9的數字,源位置目標位置')
        }
    } else {
        printMsg('🈲🚫犯規⛔️輸入指令【'+ orderStr + '】錯誤,應該爲兩位1-9的數字,源位置目標位置')
    }
}

// 處理下期指令
handleOrder = (srcIndex, destIndex) => {
    printMsg('handleOrder源位置' + srcIndex + '棋子' + Board[srcIndex] + '移動到目標位置' + destIndex + '棋子' + Board[destIndex])
    // 目標位置爲空
    if (Board[destIndex] === EmptyArmy) {
        // 雙方輪流下棋
        const army = (stepIndex % 2 === 0 ? BlackArmy : RedArmy)
        if (Board[srcIndex] === army) {
            Board[destIndex] = army
            Board[srcIndex] = EmptyArmy
            printMsg('Step' + (stepIndex++) + ': ' + srcIndex + '➡️' + destIndex)
        } else {
            printMsg('🈲🚫犯規⛔️源位置' + srcIndex + '不是你的棋子' + Board[srcIndex])
        }
    } else {
        printMsg('🈲🚫犯規⛔️目標位置' + destIndex + '已有棋子' + Board[destIndex])
    }
    printBoard()
}
shengshuqiangdeMacBook-Pro:c shengshuqiang$ mkdir build
shengshuqiangdeMacBook-Pro:c shengshuqiang$ cd build
shengshuqiangdeMacBook-Pro:build shengshuqiang$ cmake ..
-- The C compiler identification is AppleClang 10.0.0.10001145
-- The CXX compiler identification is AppleClang 10.0.0.10001145
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
-- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/learn/c/build
shengshuqiangdeMacBook-Pro:build shengshuqiang$ make
Scanning dependencies of target learn
[ 50%] Building CXX object CMakeFiles/learn.dir/shell.cc.o
[100%] Linking CXX executable learn
ld: warning: directory not found for option '-L/Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/learn/c/libs'
[100%] Built target learn
shengshuqiangdeMacBook-Pro:build shengshuqiang$ ./learn
> load('../../js/hello.js')
> init()
①💧   一      ②💧   一      ③💧
|      ╲       |      ╱       |
④🎄   一      ⑤🎄   一      ⑥🎄
|      ╱       |      ╲       |
⑦🔥   一      ⑧🔥   一      ⑨🔥

請🌴🐑
> handleOrder(1,5)
handleOrder源位置1棋子💧移動到目標位置5棋子🎄
Step0: 1➡️5
①🎄   一      ②💧   一      ③💧
|      ╲       |      ╱       |
④🎄   一      ⑤💧   一      ⑥🎄
|      ╱       |      ╲       |
⑦🔥   一      ⑧🔥   一      ⑨🔥

> quit()

操刀必割(Do It)

思路:java端啓動js初始化運行環境
2. js端輸出遊戲規則和棋局,等待java端輸入
3. java端輸入指令,js刷新棋盤並且對弈,直至高下立判
4. java端只是用戶輸入和棋盤展示終端,c端只負責透傳消息,js端負責邏輯處理

因爲CMake和Make不懂,在使用CMake將引入V8的HelloJNI.cc打成動態鏈接庫始終過不去。
在我進擊ReactNative最黑暗的時刻,是@羅佳妮女神、@雷地球和@張千一大牛指點迷津,我才能夠更上一層樓,O(∩_∩)O謝謝你們的幫助

  1. 剩下的只是花點時間和三個鍵:Ctrl、C、V
  2. 成功構建手寫ReactNative簡易版,雖然沒有React,但是已經初具的跨平臺交互能力。

    // HelloJNI.java
    public class HelloJNI {
    private Scanner scanner = new Scanner(System.in);
    
    static {
        System.loadLibrary("HelloJNI");
    }
    
    public HelloJNI() {
        scanner.useDelimiter(System.getProperty("line.separator"));
    }
    
    public native void load(String jsBundle);
    public native void sendOrder(String orderStr);
    
    public static void receiveMsg(String msg) {
        System.out.println("receiveMsg:\t" + msg);
    }
    public static void main(String[] args) {
        System.out.println("Hello SSU!");
        HelloJNI helloJni = new HelloJNI();
    
        String jsBundle = null;
        try {
            jsBundle = readFile("../hello.js");
        } catch(IOException exception) {
        }
        if (null != jsBundle) {
            helloJni.load(jsBundle);
        }
    }
    
    /**
     * 通過字符流讀取文件中的數據
     * @throws IOException
     */
     public static String readFile(String path) throws IOException{
         // 注意這裏的不同,File.separator是分隔符,這裏指明絕對路徑,即D盤根目錄下的abc.txt文件
         File file = new File(path);
         // 如果文件不存在則創建文件
         if (!file.exists()) {
             file.createNewFile();
         }
         InputStream inputStream = new FileInputStream(file);
         // 這裏也有不同,可以根據文件的大小來聲明byte數組的大小,確保能把文件讀完
         byte[] bs = new byte[(int)file.length()];
         // read()方法每次只能讀一個byte的內容
         inputStream.read(bs);
         inputStream.close();
         String fileStr = new String(bs);
         // System.out.println("##JAVA##\n" + fileStr);
         return fileStr;
     }
    
     public String waitForInputOrder() {
         if (scanner.hasNext()) {
             String order = scanner.next();
             // System.out.println("##JAVA##\n" + "waitForInputOrder:\t" + order);
             return order;
         }
         return null;
     }
    }
    // HelloJNI.cc
    void Init(JNIEnv* env, jobject jobj, const char* str) {
    // 初始化v8引擎
    char* argv = "hello";
    int argc = 1;
    v8::V8::InitializeICUDefaultLocation(argv);
    v8::V8::InitializeExternalStartupData(argv);
    std::unique_ptr<v8::Platform> platform = v8::platform::NewDefaultPlatform();
    platform = v8::platform::NewDefaultPlatform();
    v8::V8::InitializePlatform(platform.get());
    v8::V8::Initialize();
    v8::V8::SetFlagsFromCommandLine(&argc, &argv, true);
    v8::Isolate* isolate;
    v8::Isolate::CreateParams create_params;
    create_params.array_buffer_allocator = v8::ArrayBuffer::Allocator::NewDefaultAllocator();
    isolate = v8::Isolate::New(create_params);
    {
        // 初始化js上下文
        v8::Isolate::Scope isolate_scope(isolate);
        v8::HandleScope handle_scope(isolate);
        // 初始化注入可供js調用c方法上下文
        context = CreateShellContext(isolate);
        if (context.IsEmpty()) {
            fprintf(stderr, "Error creating context\n");
            return;
        }
        // printf("##C##\nInit:isolate=%p\n",isolate);
        ExecuteString(isolate, str);
        ExecuteString(isolate, "init()");
    
        static const int kBufferSize = 256;
        // Enter the execution environment before evaluating any code.
        v8::Context::Scope context_scope(context);
        v8::Local<v8::String> name(v8::String::NewFromUtf8(isolate, "(shell)", v8::NewStringType::kNormal).ToLocalChecked());
        while (true) {
            jclass cls = env->GetObjectClass(jobj);
            jmethodID mid = env->GetMethodID(cls, "waitForInputOrder", "()Ljava/lang/String;");
            if (mid == NULL) {
                return;
            }
            jstring jstr = static_cast<jstring>(env->CallObjectMethod(jobj, mid));
            const char* iputOrder = env->GetStringUTFChars(jstr, NULL);
            // printf("##C##\nJava_HelloJNI_load:iputOrder= %s\n", iputOrder);
            ExecuteString(isolate, iputOrder);
        }
    }
    // 釋放v8引擎
    isolate->Dispose();
    v8::V8::Dispose();
    v8::V8::ShutdownPlatform();
    delete create_params.array_buffer_allocator;
    }
    shengshuqiangdeMacBook-Pro:do shengshuqiang$ pwd
    /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/do
    shengshuqiangdeMacBook-Pro:do shengshuqiang$ mkdir build
    shengshuqiangdeMacBook-Pro:do shengshuqiang$ cd build
    shengshuqiangdeMacBook-Pro:build shengshuqiang$ cmake ..
    -- The C compiler identification is AppleClang 10.0.0.10001145
    -- The CXX compiler identification is AppleClang 10.0.0.10001145
    -- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc
    -- Check for working C compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc -- works
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Detecting C compile features
    -- Detecting C compile features - done
    -- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++
    -- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ -- works
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Detecting CXX compile features
    -- Detecting CXX compile features - done
    -- Configuring done
    CMake Warning (dev):
    Policy CMP0042 is not set: MACOSX_RPATH is enabled by default.  Run "cmake
    --help-policy CMP0042" for policy details.  Use the cmake_policy command to
    set the policy and suppress this warning.
    MACOSX_RPATH is not specified for the following targets:
    HelloJNI
    This warning is for project developers.  Use -Wno-dev to suppress it.
    -- Generating done
    -- Build files have been written to: /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/do/build
    shengshuqiangdeMacBook-Pro:build shengshuqiang$ make
    Scanning dependencies of target HelloJNI
    [ 50%] Building CXX object CMakeFiles/HelloJNI.dir/HelloJNI.cc.o
    In file included from /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/do/HelloJNI.cc:1:
    /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/do/HelloJNI.h:19:24: warning: 'CreateShellContext' has C-linkage specified, but returns user-defined type 'v8::Local<v8::Context>' which
      is incompatible with C [-Wreturn-type-c-linkage]
    v8::Local<v8::Context> CreateShellContext(v8::Isolate* isolate);
                       ^
    /Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/do/HelloJNI.cc:20:18: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings]
    char* argv = "hello";
                 ^
    2 warnings generated.
    [100%] Linking CXX shared library libHelloJNI.dylib
    ld: warning: directory not found for option '-L/Users/shengshuqiang/ideal/HelloJSCore/AdvanceOnReactNative/do/libs'
    [100%] Built target HelloJNI
    shengshuqiangdeMacBook-Pro:build shengshuqiang$ javac ../HelloJNI.java -d .
    shengshuqiangdeMacBook-Pro:build shengshuqiang$ java HelloJNI
    Hello SSU!
    ①♠︎   一      ②♠︎   一      ③♠︎
    |      ╲       |      ╱       |
    ④♢    一      ⑤♢    一      ⑥♢
    |      ╱       |      ╲       |
    ⑦♤    一      ⑧♤    一      ⑨♤
    請
    handleOrder(1,5)
    handleOrder源位置1棋子♠︎移動到目標位置5棋子♢
    Step0: 1➡️5
    ①♢    一      ②♠︎   一      ③♠︎
    |      ╲       |      ╱       |
    ④♢    一      ⑤♠︎   一      ⑥♢
    |      ╱       |      ╲       |
    ⑦♤    一      ⑧♤    一      ⑨♤
    handleOrder(7,4)
    handleOrder源位置7棋子♤移動到目標位置4棋子♢
    Step1: 7➡️4
    ①♢    一      ②♠︎   一      ③♠︎
    |      ╲       |      ╱       |
    ④♤    一      ⑤♠︎   一      ⑥♢
    |      ╱       |      ╲       |
    ⑦♢    一      ⑧♤    一      ⑨♤
    quit()
    shengshuqiangdeMacBook-Pro:build shengshuqiang$ 

    對角棋博弈

一張圖

手寫ReactNative架構圖

  1. 每天看一篇技術文章,發朋友圈。我先拋個磚:進擊ReactNative-納百川
  2. Just Do It。

  1. 可運行的跨平臺(Java、C、JavaScript)Demo AdvanceOnReactNative
  2. 一些有利於北漂混下去的理由

進階

  1. 進擊ReactNative-納百川

參考

  1. 深入理解JSCore
  2. 郭孝星ReactNative源碼篇
  3. 再談移動端動態化方案
  4. 美團旅行前端技術體系的思考與實踐
  5. 每個架構師都應該研究下康威定律
  6. java ScriptEngine 使用
  7. java中執行js代碼
  8. 如何在java中調用js方法
  9. JavaScriptCore全面解析 (上篇)
  10. JavaScriptCore全面解析 (下篇)

長歌

寒窯賦

北宋大臣呂蒙正

天有不測風雲,人有旦夕禍福。
蜈蚣百足,行不及蛇;雄雞兩翼,飛不過鴉。
馬有千里之程,無騎不能自往;人有沖天之志,非運不能自通。

蓋聞:人生在世,富貴不能淫,貧賤不能移。
文章蓋世,孔子厄於陳邦;武略超羣,太公釣於渭水。
顏淵命短,殊非兇惡之徒;盜跖年長,豈是善良之輩。
堯帝明聖,卻生不肖之兒;瞽叟愚頑,反生大孝之子。
張良原是布衣,蕭何稱謂縣吏。
晏子身無五尺,封作齊國宰相;孔明臥居草廬,能作蜀漢軍師。
楚霸雖雄,敗於烏江自刎;漢王雖弱,竟有萬里江山。
李廣有射虎之威,到老無封;馮唐有乘龍之才,一生不遇。
韓信未遇之時,無一日三餐,及至遇行,腰懸三尺玉印,一旦時衰,死於陰人之手。

有先貧而後富,有老壯而少衰。
滿腹文章,白髮竟然不中;才疏學淺,少年及第登科。
深院宮娥,運退反爲妓妾;風流妓女,時來配作夫人。

青春美女,卻招愚蠢之夫;俊秀郎君,反配粗醜之婦。
蛟龍未遇,潛水於魚鱉之間;君子失時,拱手於小人之下。
衣服雖破,常存儀禮之容;面帶憂愁,每抱懷安之量。
時遭不遇,只宜安貧守份;心若不欺,必然揚眉吐氣。
初貧君子,天然骨骼生成;乍富小人,不脫貧寒肌體。

天不得時,日月無光;地不得時,草木不生;水不得時,風浪不平;人不得時,利運不通。
注福注祿,命裏已安排定,富貴誰不欲?
人若不依根基八字,豈能爲卿爲相?

吾昔寓居洛陽,朝求僧餐,暮宿破窖,思衣不可遮其體,思食不可濟其飢,
上人憎,下人厭,人道我賤,非我不棄也。
今居朝堂,官至極品,位置三公,身雖鞠躬於一人之下,而列職於千萬人之上,
有撻百僚之杖,有斬鄙吝之劍,思衣而有羅錦千箱,思食而有珍饈百味,
出則壯士執鞭,入則佳人捧觴,上人寵,下人擁。
人道我貴,非我之能也,此乃時也、運也、命也。

嗟呼!人生在世,富貴不可盡用,貧賤不可自欺,聽由天地循環,周而復始焉。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章