React Native JSI:将BridgeModule转换为JSIModule

596 阅读2分钟

前言

如果你还不清楚什么是JSI可以参考上一篇文章

在上一篇文章上介绍了什么是JSI,以及如何使用JSI借助C++与RN进行通信。但是在我们原有的项目中有大量的使用OC或者Java编写的原生模块,其中的一些可以使用C++重写,但大多数模块使用了平台特有的API和SDK,他们没有对应的C++实现。

在本文中,将带领大家如何将原有的模块转化为JSI模块。本文不再讲解基础概念,如果你有不明白的地方请参考上一篇文章

使用JSI实现js与原生交互

上图描述了两端是如何进行交互的,这里面没有了React Native 的 Bridge,而是使用了C++作为中介。

  1. 在iOS端可以很简单的实现,因为OC和C++可以混编。
  2. 在Android端要麻烦一些,需要通过JNI进行C++ 与 Java的交互。

iOS端实现

首先我们在SimpleJsi.mm 中增加 getModelsetItemgetItem 用以模拟原生模块。这些方法都使用到了平台特有的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);
  };

执行结果

image-20210816113702360

总结

使用JSI进行moudle开发虽然看着有些复杂,但还是值得我们花时间去研究的。因为它的性能是最佳的,没有不必要的转换,所有的操作都是那么直接的发生在一层上。

文中的代码,你可以在公众号回复 JSI 获取GitHub 下载地址。

参考资料

blog.notesnook.com/convert-nat…