React Native JSI:将BridgeModule转换为JSIModule
我们原有的项目中有大量的使用OC或者Java编写的原生模块,其中的一些可以使用C++重写,但大多数模块使用了平台特有的API和SDK,他们没有对应的C++实现。
在本文中,将带领大家如何将原有的模块转化为JSI模块。本文不再讲解基础概念,如果你有不明白的地方请参考上一篇文章。
上图描述了两端是如何进行交互的,这里面没有了React Native 的 Bridge,而是使用了C++作为中介。
- 在iOS端可以很简单的实现,因为OC和C++可以混编。
- 在Android端要麻烦一些,需要通过JNI进行C++ 与 Java的交互。
iOS端实现
首先我们在SimpleJsi.mm
中增加 getModel
、setItem
、getItem
用以模拟原生模块。这些方法都使用到了平台特有的API。
- (NSString *)getModel {
struct utsname systemInfo;
uname(&systemInfo);
return [NSString stringWithCString:systemInfo.machine
encoding:NSUTF8StringEncoding];
}
- (void)setItem:(NSString *)key :(NSString *)value {
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
[standardUserDefaults setObject:value forKey:key];
[standardUserDefaults synchronize];
}
- (NSString *)getItem:(NSString *)key {
NSUserDefaults *standardUserDefaults = [NSUserDefaults standardUserDefaults];
return [standardUserDefaults stringForKey:key];
}
接下来我们需要实现一个新的install
方法:
static void install(facebook::jsi::Runtime &jsiRuntime, SimpleJsi *simpleJsi) {
auto getDeviceName = Function::createFromHostFunction(
jsiRuntime, PropNameID::forAscii(jsiRuntime, "getDeviceName"), 0,
[simpleJsi](Runtime &runtime, const Value &thisValue,
const Value *arguments, size_t count) -> Value {
facebook::jsi::String deviceName =
convertNSStringToJSIString(runtime, [simpleJsi getModel]);
return Value(runtime, deviceName);
});
jsiRuntime.global().setProperty(jsiRuntime, "getDeviceName", move(getDeviceName));
}
这个方法接收两个参数。其中SimpleJsi
用来调用 getModel
方法。这个方法的返回值是NSString。我们需要将其转化为JSI认识的String类型。这里我们使用了convertNSStringToJSIString
方法。这个放开来自开源代码YeetJSIUtils。
然后,我们在修改RN端,修改APP.js
const press = () => {
// setResult(global.multiply(2, 2));
// global.multiplyWithCallback(4, 5, alertResult);
alert(global.getDeviceName());
};
执行结果。
同理,我们适配一下其他两个方法。
关键的地方还是参数的获取与转换。
auto setItem = Function::createFromHostFunction(
jsiRuntime, PropNameID::forAscii(jsiRuntime, "setItem"), 2,
[simpleJsi](Runtime &runtime, const Value &thisValue,
const Value *arguments, size_t count) -> Value {
NSString *key =
convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
NSString *value =
convertJSIStringToNSString(runtime, arguments[1].getString(runtime));
[simpleJsi setItem:key :value];
return Value(true);
});
jsiRuntime.global().setProperty(jsiRuntime, "setItem", move(setItem));
auto getItem = Function::createFromHostFunction(
jsiRuntime, PropNameID::forAscii(jsiRuntime, "getItem"), 0,
[simpleJsi](Runtime &runtime, const Value &thisValue,
const Value *arguments, size_t count) -> Value {
NSString *key =
convertJSIStringToNSString(runtime, arguments[0].getString(runtime));
facebook::jsi::String value =
convertNSStringToJSIString(runtime, [simpleJsi getItem:key]);
return Value(runtime, value);
});
jsiRuntime.global().setProperty(jsiRuntime, "getItem", move(getItem));
修改App.js
const press = () => {
global.setItem('RiverLi', '大前端');
setTimeout(() => {
alert(global.getItem('RiverLi'));
}, 300);
};
执行结果
总结
使用JSI进行moudle开发虽然看着有些复杂,但还是值得我们花时间去研究的。因为它的性能是最佳的,没有不必要的转换,所有的操作都是那么直接的发生在一层上。
作者:RiverLi
链接:https://juejin.cn/post/6999799689155444773