本篇文章小编给大家分享一下React Native JSI实现RN与原生通信代码示例,文章代码介绍的很详细,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看。
什么是JSI
React Native JSI (JavaScript Interface) 可以使 JavaScript 和 原生模块 更快、更简单的通信。它也是React Native 新的架构体系中Fabric UI层 和 Turbo 模块的核心部分。
JSI有什么不同
JSI 移除了原生代码和JavaScript代码之间的桥接(bridge),同时也省去了两端相互调用时大量的JSON序列化和反序列化操作。JSI为原生和JS交互打开了新的大门。下面是一些JSI的特点:
JavaScript Interface 允许我们向JavaScript 运行时注册方法。这些方法在js环境中可以通过 global对象获取并调用。
我们完全可以使用C++或者在iOS里使用OC ,在Android里使用Java实现这些注册方法。
原先使用bridge 的方式实现的原生模块可以通过增加一层C++,快速转化为通过JSI实现。
在iOS端实现非常简单,因为C++和OC 可以方便的实现混编。
在Android中,我们需要通过JNI 做一些转化。
这些方法可以是完全同步的,这意味着不必强制使用async。await。
在iOS中使用JSI
下面我们将一步一步的在iOS工程中使用JSI实现原生与JS的通信。
创建一个新的React Native 项目
npx react-native init jsiDemo
iOS端配置
在iOS项目目录中创建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" #includeusing 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 方法创建了一个方法,并通过setProperty 方法将其注册到js运行时。
接下来,我们需要创建一个moudle, 在moudle中执行install 方法。
我们创建OC 文件,SimpleJsi.h , SimpleJsi.mm
SimpleJsi.h
#import@interface SimpleJsi : NSObject @property (nonatomic, assign) BOOL setBridgeOnMainQueue; @end
SimpleJsi.mm
#import "SimpleJsi.h" #import#import #import #import "example.h" #import 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 方法中,我们调用了 example的 install 方法,完成方法的注册。
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); }; export default App; {'调用helloword:' + result}
点击按钮之后,发现result的值为helloworld。
结果
上面我们实现了js 调用原生,但没有参数,接下来我们实现一个单参数的调用。
js调用带参数的原生方法
我们在example.cpp 的 install 方法中增加multiply方法的注册,从arguments 中取出入参。
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); }; export default App; {'2*2 = ' + result}
结果
原生调用JS
原生调用js主要通过jsiRuntime.global().getPropertyAsFunction(jsiRuntime, "jsMethod").call(jsiRuntime);方法实现。
首先我们在js中增加一个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); }; export default App; {'2*2 = ' + result}
在原生端,我们假设在进入应用的时候触发调用js方法,我们修改AppDelegate中的applicationWillEnterForeground 方法。
- (void)applicationWillEnterForeground:(UIApplication *)application { SimpleJsi *jsi = [self.bridge moduleForName:@"SimpleJsi"]; [jsi calljs]; }
通过moduleForName方法获取SimpleJsi对象, 然后通过SimpleJsi中的calljs 方法。
- (void)calljs { RCTCxxBridge *cxxBridge = (RCTCxxBridge *)self.bridge; Runtime &jsiRuntime = *(facebook::jsi::Runtime *)cxxBridge.runtime; jsiRuntime.global().getPropertyAsFunction(jsiRuntime, "jsMethod").call(jsiRuntime); }
结果
原生调用带参数的JS方法
多参数调用和无参调用类似,只是在call方法后面增加了参数列表。
首先我们先在js侧定义方法,修改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); }; export default App; {'2*2 = ' + result}
然后我们修改原生端的调用
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); }
结果
在原生端调用js的函数参数
在原生中调用js参数中的函数,需要通过arguments[i].getObject(runtime).getFunction(runtime).call(runtime, value);的方式触发。
首先我们在example.cpp 中新注册一个方法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(); //调用callback arguments[2].getObject(runtime).getFunction(runtime).call(runtime, x * y); return Value(); }); jsiRuntime.global().setProperty(jsiRuntime, "multiplyWithCallback", move(multiplyWithCallback));
在js侧进行调用, 修改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); }; export default App; {'2*2 = ' + result}
点击按钮之后,会调用multiplyWithCallback 将alertResult 方式传递给原生。
结果