☞閱讀原文
目前沒有找到讓我能達到Native開發熟練度的ReactNative文章,在@胡朝旭大神點撥“ReactNative涉及Native(Android/iOS)、C和JavaScript,業界大部分只會其一,少數會其二,全會的不是一般的人,而且他們也沒有時間寫這個”下,
我的機會來了
ReactNative涉及技術棧包含前端、客戶端、跨平臺通信,語言包含Java/Object-C、C、JavaScript。
直接看源碼肯定是一頭霧水【大神當我這句話沒說】,
我嘗試先從“原理+實踐,現學現做”的角度手寫ReactNative,加深理解。
目的是先學會怎麼用,再去想爲什麼!
情境(Situation)
約法三章
- 本文還沒有觸及到ReactNative源碼,屬於我單方面的臆想,中間雜糅一些段子和囉嗦的三觀,純粹爲博一笑。
- 本文是在AdvanceOnReactNative Demo運行通過(環境爲Mac電腦)的基礎上寫作,Native端從Java出發,目前不包含Object-C【因爲我是Android開發,不會iOS】。
- 本文是我解決問題的一種思路,會因爲我目前水平侷限,如果有其他更優解,望大神不吝賜教。
三問
- 我是誰:專注於移動互聯網的Android野生程序猿。
- 從哪來:隸屬於大前端(FontEnd、iOS、Android)戰鬥序列,和全棧小夥伴們(產品、研發(前端、後端)、測試)並肩作戰。
- 到哪去:全棧工程師。
三看
向後看
- 互聯網時代,瀏覽器一統天下,大部分需求(聊天、購物、看劇、查資料等)用瀏覽器“即開即用”。只有少量工具級軟件使用Native(Word、PPT、Excel、翻譯詞典等)開發。
- 移動互聯網時代,大部分需求有獨立應用(美團、手機百度、天貓、京東、頭條等),但是一定也有匹配的網頁版,否則你無法用微信分享出去,而且用戶手機一般會預裝上面的巨無霸應用,基本涵蓋吃喝玩樂,所以不大可能再安裝其他同質應用,實在不行先在網頁版裏面體驗一下。
- 下載安裝一個應用弊端:下載花時間、浪費流量、佔空間、影響手機性能、泄露隱私等。
- 用戶是上帝,你能讓上帝乖乖聽你的麼?
向外看
- 業界領袖Facebook 15年推出ReactNative、目前已席捲全球,蒸蒸日上;Google 17年推出Flutter,炙手可熱。國內巨擘微信小程序、阿里Weex、大衆點評Picasso等。無一不是時代洪流的弄潮兒。
- 客戶端動態化方案技術演進:Web➢Hybrid➢插件化➢熱更新➢ReactNative、Weex、小程序、Fultter。
- 公司前端人數近年來快速增長,過去App中應用主要是Native實現(H5、Android、iOS三足鼎立),現在大部分被H5取代(H5和Android&iOS平分秋色),特別是創新性需求,H5快速試錯、隨時上線造就其終端王者地位。
- 移動互聯網瞬息萬變(H5搶佔一波又一波風口,根本不用等Native後援),手機硬件性能提升、通信網絡不斷升級、各種前端方案百花齊放(離線化、AMP( Accelerated Mobile Pages ,移動頁面加速)、PWA(Progressive Web App,漸進式WEB應用)),爲前端一統天下提供了物質基礎。
向前看
- “科學技術是第一生產力。”– 鄧爺爺
- “勞動生產力是隨着科學和技術的不斷進步而不斷髮展的。” – 馬克思
- 總之,動態化方案的出現,不是爲了替代誰,更多是爲了給用戶更好的體驗,同時讓業務可以更快的迭代,並在不斷的嘗試中,給用戶帶來更好的產品。– 再談移動端動態化方案
- “時勢不可盡倚,貧窮不可盡欺,世事翻來覆去,須當周而復始。”– 寒窯賦
△ – 葉俊星 美團旅行前端技術體系的思考與實踐
三思
思危
- 前端工程師利用ReactNative、Fultter等技術開發應用,一個(前端工程師)頂兩(Android和iOS工程師)。
- 公司不會花錢養閒人(一個前端工程師就能完成的工作,幹嘛要僱兩個客戶端工程師)。
- JavaScript大行其道,一種語言,能跨端開發(前端瀏覽器應用和後端NodeJS服務)。前端工程師快速向全棧工程師演進。
- 充分挖掘客戶端計算力,對公司運營成本是巨大誘惑(想一想1億個手機的計算都在服務端,要多少服務端機器,還要解決各種網絡傳輸和負載均衡問題,這個錢能省幹嘛不省)。
思退
- 開放心態,擁抱變化。侷限於客戶端開發,下崗是早晚的事,除非你足夠牛逼。
- 知己知彼,是時候該停下客戶端腳步,去了解前端技術棧了。
思變
- 這是個客戶端向大前端轉型的機會,是邁向全棧工程師的起點。
- 客戶端工程師轉大前端工程師是富有挑戰性的工作,但好處不言而喻。
- 客戶端較前端有無法逾越的性能優勢,單純的前端開發無法發揮極致的用戶體驗,所以,天下終究是全棧工程師(客戶端、前端、服務端)的天下。相較於前端工程師學習客戶端技術,客戶端工程師學前端門檻會低很多。
- 技術殊途同歸,原理算法如出一轍,技術始終是工程師的核心競爭力之一。 – 我的導師@喬璞大神
- “天下武功,無招不破,唯快不破。”– 星爺的《功夫》
- “假如禍事不可免的話,朕情願它早點來。”– 《康熙王朝》中康熙回答周培公“萬一吳三桂造反,皇帝的心思是什麼”
△ – 《大明王朝1566》
三願
- 尋門而入:ReactNative道理我都懂,但是仍無從下手?
- 閉門合轍:手工自作簡易版ReactNative,深入分析工業機器ReactNative實現原理(驅動Virtual DOM和Native通信原理),ReactNative文章、高階組件。
- 破門而出:ReactNative是前端的一條溝,掉進去了,叫挫折,爬出來了,叫成長。
衝突(Complication)
- 我是專注於移動互聯網的北漂打工仔,前端的活我看到,但沒有學過,不會做,侷促於Android客戶端開發。
- 我團大面積鋪開ReactNative,作爲老油條,我不入地獄誰入地獄。如其束手就擒,不如奮起一搏。
- 短兵相接半月左右,一度憑藉自己多年累積的方法論和人生觀,直面殺入ReactNative源碼,硬着頭皮看了3遍大牛郭孝星的ReactNative源碼篇,JavaScript、C++、Android來回切換,各種高能預警,磕得我一臉狗血,整得我暈頭轉向、稀裏糊塗,最終身體不適,差點撲街。
- 直接debug源碼,JavaScript層代碼沒法看,一個套着一個,看了半天都沒有找到門在哪裏;Android層算找到了消息通信的往來入口。
- 卒子的命運,註定是身不由己。“今亡亦死,舉大計亦死,等死,死國可乎?”– 《陳涉世家》
- 最樸實的方法,敢於直麪人生,我開始努力切入ReactNative源碼了,目標是達到客戶端熟練度。
疑問(Question)
5W2H分析法
- What:目標是什麼,核心問題是什麼?
- Why:爲什麼要這麼做,業界的現狀是什麼?
- Who:都有哪些角色,誰是甲方,誰是乙方,受益者是誰?
- When:什麼時候的事,什麼時機切入?
- Where:場景是什麼?
- How:怎麼做到的(實現原理)?
- How Much:有什麼價值,投入產出比是多少,怎麼衡量?
答案(Answer)
三原則
“有理、有利、有節”是毛澤東在《目前抗日統一戰線中的策略問題》中提出的對敵戰爭“三原則”。
有理
- 價值:不忘初心,打破客戶端工程師邊界,超越自我,強化核心競爭力。
“開拓萬里之波濤,布國威於四方。”–明治天皇 - 目標:以ReactNative作爲演進全棧工程師的契機,深入理解背後的原理,擴大自我生存空間。
“設計不僅僅是外觀感受。設計關乎於工作原理。”–史蒂夫·喬幫主 - 問題:
- 成本大:雙端(Android和iOS)獨立開發一份,關鍵是功能還一樣,能不能一個人幹兩個人的活?
- 週期長:依賴發版(一般一個月一版,跟不上移動互聯網的風口),需要用戶升級(沒事瞎升級,整一堆根本用不上的亂七八糟功能,除了浪費我流量,就是讓手機更卡了)。
- 代價高:熱修復不成熟,難以及時止損,好羨慕後臺和前端就可以“偷偷地、悄悄地”把bug改了,客戶端即使發現了問題,等到發版修復,再小的問題也鬧得滿城風雨了。
“且欲防微杜漸,憂在未萌。”– 宋書·吳喜傳 - 體重胖:各家應用的佈局都是構建生態平臺,打造巨無霸應用,實現一桶天下。隨着業務迭代和時間這把殺豬刀,應用體重無可奈何的飆升。讓我減肥?開玩笑!你知道我爲這身材花了多少錢嗎?
- 途徑:MECE分析法(Mutually Exclusive Collectively Exhaustive,“相互獨立,完全窮盡”)。窮舉所有的可能性,比方我拿起殺豬刀,假裝庖丁解牛,手工製作一個簡易版ReactNative。
有利
- 時間:洞悉ReactNative底層原理,有助於更快更好地完成工作,節約時間。是不是面對ReactNative紅屏不知所措?遇到問題沒法深入,不得不膚淺的換一種解決方案試試?網上資料太少,知其然不知其所以然,沒法隨心所欲、遊刃有餘?
- 加薪:一分耕耘一分收穫。
- 升職:公司不會虧待你的。
- 信心:看,XXX哥果然不是蓋的;我感覺我能搞定XXX。
- 榮譽:🐂🐝、💯、👏、🌹、🎖。
有節
- 站在巨人的肩膀上,不要重複造輪子。
- 戰線不要太長,明天的你很輕鬆做到的,現在做可能有點吃力,畢竟我們還在成長,還有明天。
- 把握分寸,畢竟精力有限。
庖丁解牛
磨刀霍霍
- 模仿最小可用產品(Minimum Viable Product, MVP)理念,手工製作一個對角棋AI程序,JSX控制AI算法和UI,Java控制檯輸出棋譜和對弈交互。
“世事如棋盤,世人若棋子。這年頭,誰利用誰還不一定!”– 《畫江湖》李克用
一刀兩斷
- 軟件 = 邏輯(邏輯處理引擎,如JavaScriptCore) + 界面(渲染引擎,如WebCore) 。界面是靜態圖片(像連環畫),通過邏輯操作界面元素動態放映(像動畫片)。
- 邏輯處理引擎跟進用戶輸入進行邏輯處理,輸出界面描述文本給渲染引擎。
- 渲染引擎解析界面描述文本(Html、JSX、佈局代碼等)和圖片資源,調用系統圖形API(OpenGL等),刷新界面。
吞刀吐火
- ReactNative中JavaScript代碼處理邏輯,生成界面描述文本給到Native,Native根據界面描述文本協議,解析成界面對象,調用圖形API刷新界面。
- Java如何運行JavaScript?
Java和JavaScript直接通信
☞上網搜索個Demo,拿來主義
{:.info}
- Java可以通過ScriptEngine直接調用知悉JavaScript代碼。
- 參考java與js通信、java ScriptEngine 使用實現Java運行JS代碼HelloScriptEngine.java
-
核心代碼示例如下(完整代碼詳見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++)能相互調用。
- 通過JNI初探,跑通第一個Java和C++調用示例HelloJNI.java。
-
核心代碼示例如下(完整代碼詳見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進階
△ – 農場老馬JNI規範
JavaScript和C++通信
☞JS引擎有JSCore和V8,我選擇。
- V8源碼,當然,我是看不懂的。
- 先找幾篇文章學習一下,知其然
- 這裏,我掉到一個坑裏面了,就是自己嘗試編譯V8源碼,發現gclient根本下載不下來,一度淚奔┭┮﹏┭┮
- 糾纏了一段時間後,我抖了個小機靈,github上面搜索可運行的V8Demo代碼。
- 人間只有真情在,還真給我找到一個可用的 依賴V8動態庫運行JS代碼coderzh/v8-demo,裏面是直接引用編譯好的v8動態鏈接庫libv8.dylib、libv8_libplatform.dylib。
- 開啓了我盲人摸象的第一步。裏面用到了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,一個牛逼💯的人
- coderzh/v8-demo只能簡單運行JS字符串拼接代碼,雖然看到了曙光,但距離旭日還遙不可及。
- 再一次被命運女神眷顧,嵌入V8的核心概念和嵌入V8的核心概念1助我跑通了v8源碼中有三個例子(hello-world.cc、process.cc、shell.cc),這下前進了關鍵性的一大步,這是一個里程碑。
- hello-world.cc:簡單打印出“hello world”。
- process.cc:演示C++如何調用JavaScript函數。
- 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謝謝你們的幫助
- 剩下的只是花點時間和三個鍵:Ctrl、C、V
-
成功構建手寫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-納百川
- Just Do It。
魚
- 可運行的跨平臺(Java、C、JavaScript)Demo AdvanceOnReactNative
- 一些有利於北漂混下去的理由
進階
參考
- 深入理解JSCore
- 郭孝星ReactNative源碼篇
- 再談移動端動態化方案
- 美團旅行前端技術體系的思考與實踐
- 每個架構師都應該研究下康威定律
- java ScriptEngine 使用
- java中執行js代碼
- 如何在java中調用js方法
- JavaScriptCore全面解析 (上篇)
- JavaScriptCore全面解析 (下篇)
長歌
寒窯賦
北宋大臣呂蒙正
天有不測風雲,人有旦夕禍福。
蜈蚣百足,行不及蛇;雄雞兩翼,飛不過鴉。
馬有千里之程,無騎不能自往;人有沖天之志,非運不能自通。
蓋聞:人生在世,富貴不能淫,貧賤不能移。
文章蓋世,孔子厄於陳邦;武略超羣,太公釣於渭水。
顏淵命短,殊非兇惡之徒;盜跖年長,豈是善良之輩。
堯帝明聖,卻生不肖之兒;瞽叟愚頑,反生大孝之子。
張良原是布衣,蕭何稱謂縣吏。
晏子身無五尺,封作齊國宰相;孔明臥居草廬,能作蜀漢軍師。
楚霸雖雄,敗於烏江自刎;漢王雖弱,竟有萬里江山。
李廣有射虎之威,到老無封;馮唐有乘龍之才,一生不遇。
韓信未遇之時,無一日三餐,及至遇行,腰懸三尺玉印,一旦時衰,死於陰人之手。
有先貧而後富,有老壯而少衰。
滿腹文章,白髮竟然不中;才疏學淺,少年及第登科。
深院宮娥,運退反爲妓妾;風流妓女,時來配作夫人。
青春美女,卻招愚蠢之夫;俊秀郎君,反配粗醜之婦。
蛟龍未遇,潛水於魚鱉之間;君子失時,拱手於小人之下。
衣服雖破,常存儀禮之容;面帶憂愁,每抱懷安之量。
時遭不遇,只宜安貧守份;心若不欺,必然揚眉吐氣。
初貧君子,天然骨骼生成;乍富小人,不脫貧寒肌體。
天不得時,日月無光;地不得時,草木不生;水不得時,風浪不平;人不得時,利運不通。
注福注祿,命裏已安排定,富貴誰不欲?
人若不依根基八字,豈能爲卿爲相?
吾昔寓居洛陽,朝求僧餐,暮宿破窖,思衣不可遮其體,思食不可濟其飢,
上人憎,下人厭,人道我賤,非我不棄也。
今居朝堂,官至極品,位置三公,身雖鞠躬於一人之下,而列職於千萬人之上,
有撻百僚之杖,有斬鄙吝之劍,思衣而有羅錦千箱,思食而有珍饈百味,
出則壯士執鞭,入則佳人捧觴,上人寵,下人擁。
人道我貴,非我之能也,此乃時也、運也、命也。
嗟呼!人生在世,富貴不可盡用,貧賤不可自欺,聽由天地循環,周而復始焉。