☞阅读原文
目前没有找到让我能达到Native开发熟练度的ReactNative文章,在@胡朝旭大神点拨“ReactNative涉及Native(Android/iOS)、C和JavaScript,业界大部分只会其一,少数会其二,全会的不是一般的人,而且他们也没有时间写这个”下,
我的机会来了
ReactNative涉及技术栈包含前端、客户端、跨平台通信,语言包含Java/Object-C、C、JavaScript。
直接看源码肯定是一头雾水【大神当我这句话没说】,
我尝试先从“原理+实践,现学现做”的角度手写ReactNative,加深理解。
目的是先学会怎么用,再去想为什么!
△ – 《大明王朝1566》
“有理、有利、有节”是毛泽东在《目前抗日统一战线中的策略问题》中提出的对敌战争“三原则”。
Java和JavaScript直接通信
☞上网搜索个Demo,拿来主义
{:.info}
核心代码示例如下(完整代码详见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++)能相互调用。
核心代码示例如下(完整代码详见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,我选择。
// main.cc
{
// 初始化上下文
v8::Isolate::Scope isolate_scope(isolate);
v8::HandleScope handle_scope(isolate);
v8::Local context = v8::Context::New(isolate);
v8::Context::Scope context_scope(context);
// 传入简单JS字符串拼接代码"Hello, World!"
v8::Local source = v8::String::NewFromUtf8(isolate, "'Hello' + ', World!'", v8::NewStringType::kNormal).ToLocalChecked();
// Compile the source code.
v8::Local script = v8::Script::Compile(context, source).ToLocalChecked();
// Run the script to get the result.
v8::Local 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,一个牛逼?的人
// 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()
☞思路:java端启动js初始化运行环境
2. js端输出游戏规则和棋局,等待java端输入
3. java端输入指令,js刷新棋盘并且对弈,直至高下立判
4. java端只是用户输入和棋盘展示终端,c端只负责透传消息,js端负责逻辑处理
因为CMake和Make不懂,在使用CMake将引入V8的HelloJNI.cc打成动态链接库始终过不去。
在我进击ReactNative最黑暗的时刻,是@罗佳妮女神、@雷地球和@张千一大牛指点迷津,我才能够更上一层楼,O(∩_∩)O谢谢你们的帮助
成功构建手写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 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 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(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' which
is incompatible with C [-Wreturn-type-c-linkage]
v8::Local 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$
北宋大臣吕蒙正
天有不测风云,人有旦夕祸福。
蜈蚣百足,行不及蛇;雄鸡两翼,飞不过鸦。
马有千里之程,无骑不能自往;人有冲天之志,非运不能自通。
盖闻:人生在世,富贵不能淫,贫贱不能移。
文章盖世,孔子厄于陈邦;武略超群,太公钓于渭水。
颜渊命短,殊非凶恶之徒;盗跖年长,岂是善良之辈。
尧帝明圣,却生不肖之儿;瞽叟愚顽,反生大孝之子。
张良原是布衣,萧何称谓县吏。
晏子身无五尺,封作齐国宰相;孔明卧居草庐,能作蜀汉军师。
楚霸虽雄,败于乌江自刎;汉王虽弱,竟有万里江山。
李广有射虎之威,到老无封;冯唐有乘龙之才,一生不遇。
韩信未遇之时,无一日三餐,及至遇行,腰悬三尺玉印,一旦时衰,死于阴人之手。
有先贫而后富,有老壮而少衰。
满腹文章,白发竟然不中;才疏学浅,少年及第登科。
深院宫娥,运退反为妓妾;风流妓女,时来配作夫人。
青春美女,却招愚蠢之夫;俊秀郎君,反配粗丑之妇。
蛟龙未遇,潜水于鱼鳖之间;君子失时,拱手于小人之下。
衣服虽破,常存仪礼之容;面带忧愁,每抱怀安之量。
时遭不遇,只宜安贫守份;心若不欺,必然扬眉吐气。
初贫君子,天然骨骼生成;乍富小人,不脱贫寒肌体。
天不得时,日月无光;地不得时,草木不生;水不得时,风浪不平;人不得时,利运不通。
注福注禄,命里已安排定,富贵谁不欲?
人若不依根基八字,岂能为卿为相?
吾昔寓居洛阳,朝求僧餐,暮宿破窖,思衣不可遮其体,思食不可济其饥,
上人憎,下人厌,人道我贱,非我不弃也。
今居朝堂,官至极品,位置三公,身虽鞠躬于一人之下,而列职于千万人之上,
有挞百僚之杖,有斩鄙吝之剑,思衣而有罗锦千箱,思食而有珍馐百味,
出则壮士执鞭,入则佳人捧觞,上人宠,下人拥。
人道我贵,非我之能也,此乃时也、运也、命也。
嗟呼!人生在世,富贵不可尽用,贫贱不可自欺,听由天地循环,周而复始焉。