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

目录
  • 什么是JSI
  • JSI有什么不同
  • 在iOS中使用JSI
    • iOS端配置
    • RN端配置
    • js调用带参数的原生方法
    • 原生调用JS
    • 原生调用带参数的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里使用OC ,在Android里使用Java实现这些注册方法。
  3. 原先使用bridge 的方式实现的原生模块可以通过增加一层C++,快速转化为通过JSI实现。
  4. 在iOS端实现非常简单,因为C++和OC 可以方便的实现混编。
  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运行时。
接下来,我们需要创建一个moudle, 在moudle中执行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。

结果

上面我们实现了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;

结果

原生调用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);
}

结果

原生调用带参数的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);
}

结果

在原生端调用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 获取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原生通信内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • React Native与Android 原生通信的方法

    我们用React Native 做混合开发的时候免不了要原生和React Native 进行通信交互,这篇文章就是分享原生模块与JS传递数据的几种方式. 总的步骤可以分为如下几点: 在原生端定义Module类,继承ReactContextBaseJavaModule,在Module类里,定义交互的方法. 定义Package类,继承ReactPackage,将Module实例添加到集合. 在android继承的ReactApplication,回调实现getPackages方法,将Package实

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

    目录 什么是JSI JSI有什么不同 在iOS中使用JSI iOS端配置 RN端配置 js调用带参数的原生方法 原生调用JS 原生调用带参数的JS方法 在原生端调用js的函数参数 总结 问题 参考资料 什么是JSI React Native JSI (JavaScript Interface) 可以使 JavaScript 和 原生模块 更快.更简单的通信.它也是React Native 新的架构体系中Fabric UI层 和 Turbo 模块的核心部分. JSI有什么不同 JSI 移除了原生代

  • React Native使用fetch实现图片上传的示例代码

    本文介绍了React Native使用fetch实现图片上传的示例代码,分享给大家,具体如下: 普通网络请求参数是JSON对象 图片上传的请求参数使用的是formData对象 使用fetch上传图片代码封装如下: let common_url = 'http://192.168.1.1:8080/'; //服务器地址 let token = ''; //用户登陆后返回的token /** * 使用fetch实现图片上传 * @param {string} url 接口地址 * @param {J

  • React Native可定制底板组件Magic Sheet使用示例

    目录 正文 如何使用它 1.安装并导入 2.基本使用方法 预览 正文 一个React Native组件,通过提供一个强制性的API,可以从应用程序的任何地方(甚至在组件之外)调用,以显示一个完全可定制的底部表单,并能够等待它解决并得到一个响应. 这个库依赖于Gorhom的/bottom-sheet 的模态组件,并接受相同的道具和儿童. 如何使用它 1.安装并导入 # Yarn $ yarn add react-native-magic-sheet # NPM $ npm i react-nati

  • Qt实现UDP通信的示例代码

    目录 服务器端-单线程 客户端 测试结果-单线程 服务器端-多线程 测试结果-多线程 设想有如下场景:若干的客户端与服务器端建立连接,建立连接后,服务器端随机发送字符串给客户端,客户端打印输出.该节案例使用TCP编程. 服务器端-单线程 头文件 #pragma once // //tcp服务端-单线程处理客户端连接 #include <QAbstractSocket> #include <QObject> class QTcpServer; class SimpleTcpSocke

  • Android中js和原生交互的示例代码

    本文介绍了Android中js和原生交互的示例代码,分享给大家,具体如下: 加载webview的类 public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); JavaScriptInterf

  • python实现socket简单通信的示例代码

    首先先来简单介绍下socket: (具体更详细介绍的可以在网上找找,都讲得非常详细),这里主要是我自己的一些理解. socket是在应用层与传输层之间的一个抽象层,它的本质是编程接口,通过socket,才能实现TCP/IP协议. 它就是一个底层套件,用来处理最底层消息的接受和发送. socket翻译为套接字,可以把TCP/IP复杂的操作抽象为简单的几个接口来供应用层调用来实现进程在网络中的通信.socket起源于Unix,而Unix的基本要素之一就是"一切都为文件",即可以通过打开--

  • QT5实现UDP通信的示例代码

    目录 前言 一.UDP通信概述 二.UDP单播模式 1.接收数据 2.发送数据 总结 代码h文件 代码c文件 前言 该例程经过实际验证可以正常使用,只简单的使用UDP中的单播模式(一对一),其余模式将在后期逐步说明...... 所用测试系统在同一局域网,其中: QT版本:5.12 PC端UDP模式:单播 UDP通信目标:基于STM32F4+LWIP协议的以太网接口 一.UDP通信概述 UDP是轻量的.不可靠的.面向数据报.无连接的协议,它可以用于对可靠性要求不高的场合,和TCP通信不同,两个程序

  • C++ 实现即时通信的示例代码(直接运行)

    目录 题目 软件:VS 服务器端 客户端 题目 由于本学期上了网络编程课程,老师要求写使用Socke实现网络编程.于是参考C++多线程实现即时通信软件写出了简单版本的没有界面的即时通信软件. 软件:VS 直接上代码,需要讲解原理的,可以参考C++多线程实现即时通信软件 服务器端 //TcpServer_plus.exe #include<stdio.h> #include <Winsock2.h> #include<WS2tcpip.h> #pragma comment

  • 基于React Context实现一个简单的状态管理的示例代码

    目录 前言 封装一个父组件用来包裹其他子组件 子组件如何获取数据呢 class Component 方式 context.Consumer useContext 总结 参考 前言 在大多数情况下,我们开发项目都需要一个状态管理,方便我们在全局共享状态库,在React生态里比较流行的几个库 redux.mobx.recoil 但是对于小项目,我们完全可以自己封装一个状态管理,减少一个包的安装就可以减小打包以后的项目体积. 主要分两步: 封装一个顶层组件提供数据 子组件获取数据和更新数据 封装一个父

  • react native与webview通信的示例代码

    WebView是ReactNative中的组件 , 它可以创建一个原生的WebView,可以用于访问一个网页. 有时候我们需要在RN与WebView之间进行通信,或者进行数据传递,或者发送消息通知.这时候就要用以下知识了. 一:WebView向RN端发送数据: 首先,我们构建一个webview: <WebView ref={'webview'} source={require('./index.html')} style={{width: 375, height: 220}} onMessage

随机推荐