返回顶部
首页 > 资讯 > 前端开发 > JavaScript >React Native JSI实现RN与原生通信的示例代码
  • 383
分享到

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

2024-04-02 19:04:59 383人浏览 泡泡鱼
摘要

目录什么是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.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) {
        
        
        
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.001 * NSEC_PER_SEC),
                       dispatch_get_main_queue(), ^{
            
            [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原生通信内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: React Native JSI实现RN与原生通信的示例代码

本文链接: https://lsjlt.com/news/133359.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作