React Native JSI實(shí)現(xiàn)RN與原生通信的示例代碼
什么是JSI
React Native JSI (JavaScript Interface) 可以使 JavaScript 和 原生模塊 更快、更簡(jiǎn)單的通信。它也是React Native 新的架構(gòu)體系中Fabric UI層 和 Turbo 模塊的核心部分。
JSI有什么不同
JSI 移除了原生代碼和JavaScript代碼之間的橋接(bridge),同時(shí)也省去了兩端相互調(diào)用時(shí)大量的JSON序列化和反序列化操作。JSI為原生和JS交互打開了新的大門。下面是一些JSI的特點(diǎn):
- JavaScript Interface 允許我們向JavaScript 運(yùn)行時(shí)注冊(cè)方法。這些方法在js環(huán)境中可以通過(guò) global對(duì)象獲取并調(diào)用。
- 我們完全可以使用C++或者在iOS里使用OC ,在Android里使用Java實(shí)現(xiàn)這些注冊(cè)方法。
- 原先使用bridge 的方式實(shí)現(xiàn)的原生模塊可以通過(guò)增加一層C++,快速轉(zhuǎn)化為通過(guò)JSI實(shí)現(xiàn)。
- 在iOS端實(shí)現(xiàn)非常簡(jiǎn)單,因?yàn)镃++和OC 可以方便的實(shí)現(xiàn)混編。
- 在Android中,我們需要通過(guò)JNI 做一些轉(zhuǎn)化。
- 這些方法可以是完全同步的,這意味著不必強(qiáng)制使用async。await。
在iOS中使用JSI
下面我們將一步一步的在iOS工程中使用JSI實(shí)現(xiàn)原生與JS的通信。
創(chuàng)建一個(gè)新的React Native 項(xiàng)目
npx react-native init jsiDemo
iOS端配置
在iOS項(xiàng)目目錄中創(chuàng)建C++文件,example.h、 example.cpp。
example.h
#ifndef EXAMPLE_H
#define EXAMPLE_H
namespace facebook {
namespace jsi {
class Runtime;
}
}
namespace example {
void install(facebook::jsi::Runtime &jsiRuntime);
}
#endif /* EXAMPLE_H */
example.m
#include "example.h"
#include <jsi/jsi.h>
using namespace facebook::jsi;
using namespace std;
namespace example {
void install(Runtime &jsiRuntime) {
auto helloWorld = Function::createFromHostFunction(jsiRuntime,
PropNameID::forAscii(jsiRuntime,
"helloWorld"),
0,
[](Runtime &runtime,
const Value &thisValue,
const Value *arguments,
size_t count) -> Value {
string helloworld = "helloworld";
return Value(runtime, String::createFromUtf8(runtime,helloworld));
});
jsiRuntime.global().setProperty(jsiRuntime, "helloWorld", move(helloWorld));
}
}
在上面的代碼中,我們使用 createFromHostFunction 方法創(chuàng)建了一個(gè)方法,并通過(guò)setProperty 方法將其注冊(cè)到j(luò)s運(yùn)行時(shí)。
接下來(lái),我們需要?jiǎng)?chuàng)建一個(gè)moudle, 在moudle中執(zhí)行install 方法。
我們創(chuàng)建OC 文件,SimpleJsi.h , SimpleJsi.mm
SimpleJsi.h
#import <React/RCTBridgeModule.h> @interface SimpleJsi : NSObject <RCTBridgeModule> @property (nonatomic, assign) BOOL setBridgeOnMainQueue; @end
SimpleJsi.mm
#import "SimpleJsi.h"
#import <React/RCTBridge+Private.h>
#import <React/RCTUtils.h>
#import <jsi/jsi.h>
#import "example.h"
#import <sys/utsname.h>
using namespace facebook::jsi;
using namespace std;
@implementation SimpleJsi
@synthesize bridge = _bridge;
@synthesize methodQueue = _methodQueue;
RCT_EXPORT_MODULE()
+ (BOOL)requiresMainQueueSetup {
return YES;
}
- (void)setBridge:(RCTBridge *)bridge {
_bridge = bridge;
_setBridgeOnMainQueue = RCTIsMainQueue();
[self installLibrary];
}
- (void)installLibrary {
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
if (!cxxBridge.runtime) {
/**
* This is a workaround to install library
* as soon as runtime becomes available and is
* not recommended. If you see random crashes in iOS
* global.xxx not found etc. use this.
*/
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC),
dispatch_get_main_queue(), ^{
/**
When refreshing the app while debugging, the setBridge
method is called too soon. The runtime is not ready yet
quite often. We need to install library as soon as runtime
becomes available.
*/
[self installLibrary];
});
return;
}
example::install(*(facebook::jsi::Runtime *)cxxBridge.runtime);
}
@end
在 setBridge 方法中,我們調(diào)用了 example的 install 方法,完成方法的注冊(cè)。
RN端配置
修改App.js
import React from 'react';
import type {Node} from 'react';
import {Text, View, Button} from 'react-native';
const App: () => Node = () => {
const [result, setResult] = React.useState();
const press = () => {
setResult(global.helloWorld());
};
return (
// eslint-disable-next-line react-native/no-inline-styles
<View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
<View style={{height: '10%'}} />
<Button onPress={press} title="按鈕" />
<Text>{'調(diào)用helloword:' + result}</Text>
</View>
);
};
export default App;
點(diǎn)擊按鈕之后,發(fā)現(xiàn)result的值為helloworld。
結(jié)果

上面我們實(shí)現(xiàn)了js 調(diào)用原生,但沒(méi)有參數(shù),接下來(lái)我們實(shí)現(xiàn)一個(gè)單參數(shù)的調(diào)用。
js調(diào)用帶參數(shù)的原生方法
我們?cè)趀xample.cpp 的 install 方法中增加multiply方法的注冊(cè),從arguments 中取出入?yún)ⅰ?br />
auto multiply = Function::createFromHostFunction(jsiRuntime,
PropNameID::forAscii(jsiRuntime,
"multiply"),
2,
[](Runtime &runtime,
const Value &thisValue,
const Value *arguments,
size_t count) -> Value {
int x = arguments[0].getNumber();
int y = arguments[1].getNumber();
return Value(x * y);
});
jsiRuntime.global().setProperty(jsiRuntime, "multiply", move(multiply));
然后修改App.js
import React from 'react';
import type {Node} from 'react';
import {Text, View, Button} from 'react-native';
const App: () => Node = () => {
const [result, setResult] = React.useState();
const press = () => {
setResult(global.multiply(2, 2));
};
return (
// eslint-disable-next-line react-native/no-inline-styles
<View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
<View style={{height: '10%'}} />
<Button onPress={press} title="按鈕" />
<Text>{'2*2 = ' + result}</Text>
</View>
);
};
export default App;
結(jié)果

原生調(diào)用JS
原生調(diào)用js主要通過(guò)jsiRuntime.global().getPropertyAsFunction(jsiRuntime, "jsMethod").call(jsiRuntime);方法實(shí)現(xiàn)。
首先我們?cè)趈s中增加一個(gè)js方法。我們修改App.js 在上面增加jsMethod 方法。
import React from 'react';
import type {Node} from 'react';
import {Text, View, Button} from 'react-native';
const App: () => Node = () => {
const [result, setResult] = React.useState();
global.jsMethod = () => {
alert('hello jsMethod');
};
const press = () => {
setResult(global.multiply(2, 2));
};
return (
// eslint-disable-next-line react-native/no-inline-styles
<View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
<View style={{height: '10%'}} />
<Button onPress={press} title="按鈕" />
<Text>{'2*2 = ' + result}</Text>
</View>
);
};
export default App;
在原生端,我們假設(shè)在進(jìn)入應(yīng)用的時(shí)候觸發(fā)調(diào)用js方法,我們修改AppDelegate中的applicationWillEnterForeground 方法。
- (void)applicationWillEnterForeground:(UIApplication *)application {
SimpleJsi *jsi = [self.bridge moduleForName:@"SimpleJsi"];
[jsi calljs];
}
通過(guò)moduleForName方法獲取SimpleJsi對(duì)象, 然后通過(guò)SimpleJsi中的calljs 方法。
- (void)calljs {
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
Runtime &jsiRuntime = *(facebook::jsi::Runtime *)cxxBridge.runtime;
jsiRuntime.global().getPropertyAsFunction(jsiRuntime, "jsMethod").call(jsiRuntime);
}
結(jié)果

原生調(diào)用帶參數(shù)的JS方法
多參數(shù)調(diào)用和無(wú)參調(diào)用類似,只是在call方法后面增加了參數(shù)列表。
首先我們先在js側(cè)定義方法,修改app.js
import React from 'react';
import type {Node} from 'react';
import {Text, View, Button} from 'react-native';
const App: () => Node = () => {
const [result, setResult] = React.useState();
global.jsMethod = () => {
alert('hello jsMethod');
};
global.jsMultiply = (x, y) => {
alert('x * y = ' + x * y);
};
const press = () => {
setResult(global.multiply(2, 2));
};
return (
// eslint-disable-next-line react-native/no-inline-styles
<View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
<View style={{height: '10%'}} />
<Button onPress={press} title="按鈕" />
<Text>{'2*2 = ' + result}</Text>
</View>
);
};
export default App;
然后我們修改原生端的調(diào)用
AppDelegate.m
- (void)applicationWillEnterForeground:(UIApplication *)application {
SimpleJsi *jsi = [self.bridge moduleForName:@"SimpleJsi"];
// [jsi calljs];
[jsi callJsMultiply:4 y:4];
}
SimpleJsi.m
- (void)callJsMultiply:(int)x y:(int) y {
RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge;
Runtime &jsiRuntime = *(facebook::jsi::Runtime *)cxxBridge.runtime;
jsiRuntime.global().getPropertyAsFunction(jsiRuntime, "jsMultiply").call(jsiRuntime, x, y);
}
結(jié)果

在原生端調(diào)用js的函數(shù)參數(shù)
在原生中調(diào)用js參數(shù)中的函數(shù),需要通過(guò)arguments[i].getObject(runtime).getFunction(runtime).call(runtime, value);的方式觸發(fā)。
首先我們?cè)趀xample.cpp 中新注冊(cè)一個(gè)方法multiplyWithCallback
auto multiplyWithCallback = Function::createFromHostFunction(jsiRuntime,
PropNameID::forAscii(jsiRuntime,
"multiplyWithCallback"),
3,
[](Runtime &runtime,
const Value &thisValue,
const Value *arguments,
size_t count) -> Value {
int x = arguments[0].getNumber();
int y = arguments[1].getNumber();
//調(diào)用callback
arguments[2].getObject(runtime).getFunction(runtime).call(runtime, x * y);
return Value();
});
jsiRuntime.global().setProperty(jsiRuntime, "multiplyWithCallback", move(multiplyWithCallback));
在js側(cè)進(jìn)行調(diào)用, 修改app.js
import React from 'react';
import type {Node} from 'react';
import {Text, View, Button} from 'react-native';
const App: () => Node = () => {
const [result, setResult] = React.useState();
global.jsMethod = () => {
alert('hello jsMethod');
};
global.jsMultiply = (x, y) => {
alert('x * y = ' + x * y);
};
const press = () => {
// setResult(global.multiply(2, 2));
global.multiplyWithCallback(4, 5, alertResult);
};
const alertResult = res => {
alert(res);
};
return (
// eslint-disable-next-line react-native/no-inline-styles
<View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
<View style={{height: '10%'}} />
<Button onPress={press} title="按鈕" />
<Text>{'2*2 = ' + result}</Text>
</View>
);
};
export default App;
點(diǎn)擊按鈕之后,會(huì)調(diào)用multiplyWithCallback 將alertResult 方式傳遞給原生。
結(jié)果

總結(jié)
上面就是本文對(duì)JSI 的介紹,文中的代碼,你可以在公眾號(hào)回復(fù) JSI 獲取GitHub 下載地址。
問(wèn)題
在RN Debug的情況下,global.xx 無(wú)法找到對(duì)應(yīng)的方法,個(gè)人也沒(méi)有頭緒,如果你有方法解決,請(qǐng)聯(lián)系我,非常感謝。
參考資料
https://blog.notesnook.com/getting-started-react-native-jsi/
reactnative.maxieewong.com/
https://github.com/react-native-community/discussions-and-proposals/issues/91
ospfranco.com/
到此這篇關(guān)于React Native JSI實(shí)現(xiàn)RN與原生通信的示例代碼的文章就介紹到這了,更多相關(guān)React Native原生通信內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
微信小程序MUI導(dǎo)航欄透明漸變功能示例(通過(guò)改變r(jià)gba的a值實(shí)現(xiàn))
這篇文章主要介紹了微信小程序MUI導(dǎo)航欄透明漸變功能,結(jié)合實(shí)例形式分析了通過(guò)改變r(jià)gba的a值實(shí)現(xiàn)透明度漸變功能的相關(guān)操作技巧,需要的朋友可以參考下2019-01-01
解決JS內(nèi)存泄露之js對(duì)象和dom對(duì)象互相引用問(wèn)題
這篇文章主要介紹了解決JS內(nèi)存泄露之js對(duì)象和dom對(duì)象互相引用問(wèn)題,需要的朋友可以參考下2017-06-06
javascript正則表達(dá)式之分組概念與用法實(shí)例
這篇文章主要介紹了javascript正則表達(dá)式之分組概念與用法,結(jié)合實(shí)例形式分析了javascript正則表達(dá)式分組的功能、定義與使用方法,需要的朋友可以參考下2016-06-06
js中數(shù)組(Array)的排序(sort)注意事項(xiàng)說(shuō)明
本篇文章主要是對(duì)js中數(shù)組(Array)的排序(sort)注意事項(xiàng)進(jìn)行了說(shuō)明介紹,需要的朋友可以過(guò)來(lái)參考下,希望對(duì)大家有所幫助2014-01-01
學(xué)習(xí)JavaScript設(shè)計(jì)模式之迭代器模式
這篇文章主要為大家介紹了JavaScript設(shè)計(jì)模式中的迭代器模式,對(duì)JavaScript設(shè)計(jì)模式感興趣的小伙伴們可以參考一下2016-01-01
JS動(dòng)態(tài)生成年份和月份實(shí)例代碼
本文給大家分享兩段代碼來(lái)通過(guò)js動(dòng)態(tài)生成年份和月份功能,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友參考下2017-02-02

