React Native 桥接原生模块

Posted rogerwu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了React Native 桥接原生模块相关的知识,希望对你有一定的参考价值。

原生模块简介

有时候一个 RN 应用需要访问一个原生平台的 API 比如相机,但是,默认情况下 JavaScript 是无法访问原生 API 的。

原生模块系统暴露了一些 Java 类的实例对象给 JavaScript,这样就可以允许开发者在 JS 代码中执行一些特定的原生代码。

简单来说,桥接原生就是为了实现 react 层 JS 实现不了的需求,比如:

  • 复杂、高性能组件:复杂表格、视频播放等;
  • 原生层开发能力:传感器编程、widget等;
  • 平台属性:系统信息、设备信息等;
  • 对接三方应用:相机、相册、地图等;

 

桥接原生方法

◆ 编写并注册原生层方法

1、使用 Android Studio 编辑器依次找到 android > app > src > main > java > com.rndemo (RN应用的项目名称) > MainApplication.java

找到 getPackages 这个方法,可以看到该方法返回的是 ReactNative 桥接的各种包,当前还没有添加任何自定义的包

 

2、在上一步的 com.rndemo 这个包下新建一个 rn 文件夹,然后在 rn 目录下新建一个 RnDemoPackage.java

 

3、新建好的这个类需要实现 ReactPackage 接口,该接口会重写两个方法 createNativeModulescreateViewManagers

createNativeModules:用来导入 RN 原生模块

createViewManagers:用来导入 RN 原生组件

 

4、从图中可以看到 createNativeModules 方法返回的是一个原生模块(NativeModule)的集合,因此,这里先新建一个原生模块,取名为 AppModule.java

 

该类需要继承 ReactContextBaseJavaModule,并且重写 getName 方法

 

同时,AppModule 还需要一个构造函数,这里可以同时按住键盘 Alt + Insert,在弹出的窗口中选择 Constructor

 

Choose Super Class Constructor 窗口中提供了 2 个构造函数,这里选择第 2 个带参的构造函数

 

5、假如 AppModule 这个原生模块需要给 JavaScript 暴露一个打开手机相册的方法,这里可以给 AppModule 添加一个 openGallery 方法,并且添加注解 @ReactMethod 用来修饰 JS 调用的原生方法

 

至于方法中使用到的 DeviceUtil 这个工具类是我们提前准备好的,其位置在 rn 同级的 utils 目录下

 

最终原生模块 AppModule 的代码如下

package com.rndemo.rn;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.rndemo.utils.DeviceUtil;

public class AppModule extends ReactContextBaseJavaModule 
    public AppModule(@Nullable ReactApplicationContext reactContext) 
        super(reactContext);
    

    @NonNull
    @Override
    public String getName() 
    // 返回原生模块注册时的名称,也就是说从 react 层的 js 代码中调用 AppModule 这个原生模块,必须先知道这个模块叫什么名称
    // 这里约定 AppModule 原生模块暴露的名字叫 CustomApp,也可以叫其它的
        return "CustomApp";
    

    @ReactMethod
    public void openGallery() 
        // 判空
        if (getCurrentActivity() == null) 
            return;
        
        DeviceUtil.openGallery(getCurrentActivity());
    

 

6、回到上一步新建的 RnDemoPackage 类中,createNativeModules 这个方法返回的是一个原生模块(NativeModule)的集合,这里可以将刚才注册好的 AppModule 注册进来

 

7、最后,回到 MainApplication.java,把 RnDeomoPackage 注册进去

 

◆ JS 层调用原生方法

可以从 NativeModules 中取出注册好的原生模块,调用原生模块的方法

import React from \'react\';
import StyleSheet, View, Button, NativeModules from \'react-native\';

export default () => 
  return (
    <View style=styles.root>
      <Button
        title="桥接原生方法"
        onPress=() => 
          // 从原生模块中取出前面注册好的 AppModule,该模块的名称为 CustomApp
          const CustomApp = NativeModules;
          // 调用原生模块的方法
          CustomApp?.openGallery();
        
      />
    </View>
  );
;

const styles = StyleSheet.create(
  root: 
    width: \'100%\',
    height: \'100%\',
  ,
);

 

上例中,点击按钮就会读取相册,由于谷歌模拟器的效果不是很直观,这里直接展示手机模拟器的效果图

 

原生模块暴露带返回值的方法

上面的示例中,提供了一个没有返回值的 getGallery 方法供 JS 层调用,那如果有返回值的方法该怎么写呢?

这里需要特别注意一点,从 JS 层调用原生层的方法是一个异步的过程,所以,原生层中带返回值的方法不能直接返回,而是要用 Promise 来实现

原生层提供一个返回应用版本名称的方法 getVersionName

 

JS 层读取版本名称

 

重启项目以后,运行效果如下图

 

 

桥接原生常量

 

◆ 编写并注册原生常量方法

在 AppModule 原生组件中重写一个 getConstants 方法

 

◆ JS 层获取原生常量(同步获取)

在 JS 层可以直接读取原生组件的常量,无需异步获取

 

重启项目以后,运行效果如下图

 

React Native在原生和React Native间通信

一、从React Native中调用原生方法(原生模块)

  原生模块是JS中也可以使用的Objective-C类。一般来说这样的每一个模块的实例都是在每一次通过JS bridge通信时创建的。他们可以导出任意的函数和常量给React Native。相关细节可以参阅这篇文章

  在React Native中,一个“原生模块”就是一个实现了“RCTBridgeModule”协议的Objective-C类,其中RCT是ReaCT的缩写。

// CalendarManager.h
#import <React/RCTBridgeModule.h>
#import <React/RCTLog.h>

@interface CalendarManager : NSObject <RCTBridgeModule>
@end

  为了实现RCTBridgeModule协议,你的类需要包含RCT_EXPORT_MODULE()宏。这个宏也可以添加一个参数用来指定在Javascript中访问这个模块的名字。如果你不指定,默认就会使用这个Objective-C类的名字。

// CalendarManager.m
@implementation CalendarManager

RCT_EXPORT_MODULE();

@end


//支付宝支付
RCT_EXPORT_METHOD(onAliPay:(NSString *)orderString  resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) 
  //模块代码  

  现在从Javascript里可以这样调用这个方法:

import 
    NativeModules
 from ‘react-native‘;
var pay = NativeModules.ReactNativePay;

 Pay.onAliPay(payStr) .then((message)=>
      console.log("message" + message); if(message !== "")
        //支付成功的处理
      this.refs.toast.show(message, DURATION.LENGTH_SHORT);
    ) .catch(e=>
      console.log("e.message" + e.message);
      if(e.message !== "") this.refs.toast.show(e.message, DURATION.LENGTH_SHORT);
      if(e.code === ‘-1‘ || e.message === ‘支付失败‘) 
        //支付失败
      
    )

  注意: 导出到Javascript的方法名是Objective-C的方法名的第一个部分。React Native还定义了一个RCT_REMAP_METHOD()宏,它可以指定Javascript方法名。当许多方法的第一部分相同的时候用它来避免在Javascript端的名字冲突。

二、参数类型

  RCT_EXPORT_METHOD 支持所有标准JSON类型,包括:

  • string (NSString)
  • number (NSIntegerfloatdoubleCGFloatNSNumber)
  • boolean (BOOLNSNumber)
  • array (NSArray) 包含本列表中任意类型
  • object (NSDictionary) 包含string类型的键和本列表中任意类型的值
  • function (RCTResponseSenderBlock)

  除此以外,任何RCTConvert类支持的的类型也都可以使用(参见RCTConvert了解更多信息)。RCTConvert还提供了一系列辅助函数,用来接收一个JSON值并转换到原生Objective-C类型或类。如下:

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)secondsSinceUnixEpoch)

  NSDate *date = [RCTConvert NSDate:secondsSinceUnixEpoch];


或

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSString *)ISO8601DateString)

  NSDate *date = [RCTConvert NSDate:ISO8601DateString];

三、回调函数

  原生模块还支持一种特殊的参数——回调函数。它提供了一个函数来把返回值传回给JavaScript。如下:

typedef void (^RCTResponseSenderBlock)(NSArray *response);

typedef void (^RCTResponseErrorBlock)(NSError *error);

typedef void (^RCTPromiseResolveBlock)(id result);

typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);

 

以上是关于React Native 桥接原生模块的主要内容,如果未能解决你的问题,请参考以下文章

React Native iOS原生模块开发实战|教程|心得|如何创建React Native iOS原生模块

React Native Android原生模块开发实战|教程|心得|怎样创建React Native Android原生模块

React Native Android原生模块开发实战|教程|心得|如何创建React Native Android原生模块

react native 接入支付宝支付

React Native在原生和React Native间通信

React Native:Android 原生模块