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 接口,该接口会重写两个方法 createNativeModules 和 createViewManagers
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 (
NSInteger
,float
,double
,CGFloat
,NSNumber
) - boolean (
BOOL
,NSNumber
) - 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原生模块