React Native JSI实现RN与原生通信的示例代码

作者:北岛

时间:2026-10-24

浏览:499

本文详解React Native JSI(JavaScript Interface)原理及在iOS和Android端的配置方法。通过C++、OC及Java代码示例,演示无参、带参及回调函数的双向通信实现,解决传统Bridge序列化开销问题,提供完整的原生调用JS及JS调用原生的实战代码。

文章目录

什么是JSI

React Native JSI(JavaScript Interface)旨在实现JavaScript与原生模块之间更高效、更简便的通信机制。它同时也是React Native新架构体系中Fabric UI层和Turbo模块的核心组成部分。

JSI有什么不同

JSI移除了原生代码与JavaScript代码之间的桥接(bridge)层,从而省去了双向调用时大量的JSON序列化和反序列化操作。JSI为原生与JS的交互开辟了新的途径。以下是JSI的主要特点:

  1. JavaScript Interface允许开发者向JavaScript运行时注册方法。这些注册的方法可以在JS环境中通过global对象获取并直接调用。
  2. 开发者可以使用C++,或在iOS中使用Objective-C、在Android中使用Java来实现这些注册方法。
  3. 原先通过bridge方式实现的原生模块,只需增加一层C++封装,即可快速转换为通过JSI实现。
  4. 在iOS端实现相对简单,因为C++与Objective-C可以方便地进行混编。
  5. 在Android端,则需要通过JNI进行一些转换处理。
  6. 这些方法可以是完全同步的,这意味着调用时无需强制使用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"
#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方法创建了一个函数,并通过setProperty方法将其注册到JS运行时。 接下来,我们需要创建一个module,并在其中执行install方法。 我们创建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方法中,我们调用了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
    <View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
      <View style={{height: '10%'}} />
      <Button onPress={press} title="按钮" />
      <Text>{'调用helloword:' + result}</Text>
    </View>
  );
};

export default App;

点击按钮后,发现result的值为helloworld。

结果

什么是JSI

上述步骤实现了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
    <View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
      <View style={{height: '10%'}} />
      <Button onPress={press} title="按钮" />
      <Text>{'2*2 = ' + result}</Text>
    </View>
  );
};

export default App;

结果

什么是JSI

原生调用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
    <View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
      <View style={{height: '10%'}} />
      <Button onPress={press} title="按钮" />
      <Text>{'2*2 = ' + result}</Text>
    </View>
  );
};

export default App;

在原生端,假设在进入应用时触发调用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);
}

结果

什么是JSI

原生调用带参数的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
    <View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
      <View style={{height: '10%'}} />
      <Button onPress={press} title="按钮" />
      <Text>{'2*2 = ' + result}</Text>
    </View>
  );
};

export default App;

然后我们修改原生端的调用代码。 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);
}

结果

什么是JSI

在原生端调用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
    <View style={{backgroundColor: '#FFFFFF', height: '100%'}}>
      <View style={{height: '10%'}} />
      <Button onPress={press} title="按钮" />
      <Text>{'2*2 = ' + result}</Text>
    </View>
  );
};

export default App;

点击按钮之后,会调用multiplyWithCallback并将alertResult方式传递给原生。

结果

什么是JSI

总结

以上就是本文对JSI的介绍,文中的代码,你可以在公众号回复JSI获取GitHub下载地址。

问题

在RN Debug模式下,global.xx无法找到对应的方法,个人目前也没有头绪。如果你有解决方法,请联系我,非常感谢。

参考资料

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/

到此这篇关于React Native JSI实现RN与原生通信的示例代码的文章就介绍到这了,更多相关React Native原生通信内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章