BG:为什么需要封装原生module?
有时候 App 需要访问平台 API,但 React Native 可能还没有相应的模块封装;或者你需要复用 Objective-C、Swift 或 C++代码,而不是用 JavaScript 重新实现一遍;又或者你需要实现某些高性能、多线程的代码,譬如图片处理、数据库、或者各种高级扩展等等。
React Native 的高级的特性:可以在其基础上编写真正的原生代码,并且可以访问平台所有的能力。这是一个相对高级的特性,我们并不认为它应当在日常开发的过程中经常出现,但具备这样的能力是很重要的。如果 React Native 还不支持某个你需要的原生特性,你应当可以自己实现该特性的封装。
总结简单一句话就是:如果 React Native 不能满足你,那你就去自定义module。
闲话少说,开始实战,下面我们以实现调用原生的手电筒开关功能为例,此例子将展现RN和原生的双向通信:
iOS平台:
在 React Native 中,一个“iOS原生模块”就是一个实现了“RCTBridgeModule”协议的 Objective-C 类,其中 RCT 是 ReaCT 的缩写。
类名我们暂定为CameraControlUtil。
1.定义头文件
#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface CameraControlUtil : NSObject<RCTBridgeModule>
@end
为了实现RCTBridgeModule协议,你的类需要包含RCT_EXPORT_MODULE()宏。这个宏也可以添加一个参数用来指定在 JavaScript 中访问这个模块的名字。如果你不指定,默认就会使用这个 Objective-C 类的名字。如果类名以 RCT 开头,则 JavaScript 端引入的模块名会自动移除这个前缀。
2.模块导出
#import "CameraControlUtil.h"
#import <AVFoundation/AVFoundation.h>
@interface CameraControlUtil ()
@property (nonatomic,assign) BOOL lightOn;//手电筒状态,true:开启,false:关闭
@end
@implementation CameraControlUtil
// 导出模块,不添加参数即默认为这个类名
RCT_EXPORT_MODULE();
@end
3.方法定义
你必须明确的声明要给 JavaScript 导出的方法,否则 React Native 不会导出任何方法。声明通过RCT_EXPORT_METHOD()宏来实现:
这里我们定义方法名:devicesetTorchOn;
入参:
state(控制手电筒开关,true:打开,false:关闭);
成功block回调:successCallback(是否成功,true:成功,false:失败);
失败block回调:failCallback(失败回调)
注意:1.JavaScript 方法名
导出到 JavaScript 的方法名是 Objective-C 的方法名的第一个部分。React Native 还定义了一个RCT_REMAP_METHOD()宏,它可以指定 JavaScript 方法名。因为 JavaScript 端不能有同名不同参的方法存在,所以当原生端存在重载方法时,可以使用这个宏来避免在 JavaScript 端的名字冲突。
注意:2.返回值类型必须是void
2.桥接到 JavaScript 的方法返回值类型必须是void。React Native 的桥接操作是异步的,所以要返回结果给 JavaScript,你必须通过回调或者触发事件来进行
/**
操作手电筒方法
@param state 控制手电筒开关,true:打开,false:关闭
@param 操作结果的回调successCallback:是否成功,failCallback:失败回调)
*/
RCT_EXPORT_METHOD(devicesetTorchOn:(BOOL)state resolver:(RCTResponseSenderBlock)successCallback resolver:(RCTResponseSenderBlock)failCallback){
NSString *callbackData = @"操作成功";
BOOL isSucc = YES;
_lightOn = !_lightOn;//可以定义一个变量记录手电筒的状态,也可以根据自己需要使用入参的state控制手电筒开闭。
Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice");
if(captureDeviceClass !=nil) {
AVCaptureDevice*device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
if([device hasTorch]) { // 判断是否有手电筒
// 请求独占访问硬件设备
[device lockForConfiguration:nil];
if(_lightOn) {
[device setTorchMode:AVCaptureTorchModeOn];//手电筒开
}else{
[device setTorchMode:AVCaptureTorchModeOff]; // 手电筒关
}
// 请求解除独占访问硬件设备
[device unlockForConfiguration];
}else{
isSucc = NO;
callbackData = @"手电筒不可用!";
}
}else{
isSucc = NO;
callbackData = @"手电筒不可用!";
}
//准备回调回去的数据
successCallback(@[[[NSNumber alloc]initWithBool:isSucc]]);
// callback(@[[[NSNumber alloc]initWithBool:isSucc],callbackData]);
failCallback(@[callbackData]);
}
如果只想写一个回调,可以通过数组组装自己的任意回调,
比如:
callback(@[[[NSNumber alloc]initWithBool:isSucc],callbackData]);
4.Javascript 中进行调用
***导入模块写法一:
import {
NativeModules
} from 'react-native';
***导入模块写法二:
var CameraControlUtil = require('react-native').NativeModules.CameraControlUtil;
***devicesetTorchOn方法调用:
//在按钮事件onPressConfirmBtn中调用
onPressConfirmBtn = (open)=>{
NativeModules.CameraControlUtil.devicesetTorchOn((!this.state.flashlightPress),(success) => {
//操作成功
Utils.consoleLog(`打开手电筒成功--${success}`);
...//你的成功处理
},(errorMsg) =>{
//操作失败,你的失败处理,可以弹框提示
Utils.consoleLog(`打开手电筒errorMsg--${errorMsg}`);
Alert.alert(
'提示',
errorMsg,
[
{text: '我知道了' onPress: () => Utils.consoleLog('操作结果alert--press i know')}
])
})
}
Android平台:
1.创建模块
本着开发维护方便,在实现某个原生module的时候,我们最好在iOS平台和Android平台上定义相同的module名字以及method名字,最好连回调的数据也同一格式,这样js调用的时候不同平台就不用作不同的处理了。当然,某些特定的情况下,我们也需要提供不同的方法及回调~~~
我们在app的java文件夹下创建一个ModuleForRN文件夹,同一管理整个app中需要的原生module。我们同样创建一个名为CameraControlUtil的module:
package com.hyproducerrn.ModuleForRN;//hyproducerrn为你自己的项目包名
//android依赖
import android.content.Context;
import android.hardware.Camera;
import android.hardware.camera2.CameraManager;
import android.os.Build;
import android.util.Log;
//RN module依赖
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
public class CameraControlUtil extends ReactContextBaseJavaModule{
...模块导出、模块实现
}
2.模块实现
2.1构建方法
private Camera camera;
private Boolean isLightOn = false;//记录手电筒状态
private final ReactApplicationContext myReactContext;
//构建方法
public CameraControlUtil(ReactApplicationContext reactContext) {
super(reactContext);
this.myReactContext = reactContext;
}
2.2模块名称
/**
* 返回一个模块名称,rn通过NativeModules可以调用此模块
*/
@Override
public String getName() {
return "CameraControlUtil";
}
2.3模块实现
/**
* @param state 控制手电筒开关,true:打开,false:关闭
* @param successCallback 打开成功的回调
* @param failCallback 打开失败的回调
*/
@ReactMethod
public void devicesetTorchOn(Boolean state, Callback successCallback, Callback failCallback) {
Camera.Parameters params;
if (!isLightOn) {
camera = Camera.open();
params = camera.getParameters();
params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
camera.setParameters(params);
camera.startPreview();
isLightOn = true;
} else {
params = camera.getParameters();
params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
camera.setParameters(params);
camera.stopPreview();
camera.release();
isLightOn = false;
}
successCallback.invoke(true);
failCallback.invoke("无错误");
}
3.模块注册
3.1 在ModuleForRN文件夹下新建CustomModuleForRNPackage.java,实现ReactPackage的两个方法,在createNativeModules里添加注册CameraControlUtil模块:
package com.hyproducerrn.ModuleForRN;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CustomModuleForRNPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
//所有自定义module都写在这里
modules.add(new OpenSettingsModule(reactContext));//打开设置页面
modules.add(new AppInfo(reactContext));//获取app版本等信息
modules.add(new CameraControlUtil(reactContext));//相机闪光灯控制
return modules;
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
3.2注册CustomModuleForRNPackage
在MainApplication.java的getPackages方法里添加刚才的CustomModuleForRNPackage包:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new CustomModuleForRNPackage(),//添加自定义module包
);
}
4.Javascript 中进行调用
由于我们在Android和iOS平台下的module名字和method名字及回调都一样,因此RN调用时,就跟iOS的调用一模一样了,9️⃣不再写一遍啦~~~~😁
以上就是React Native自定义module的介绍了,希望对你有帮助,如有欢迎大家指正错误,欢迎大家留言交流~~O(∩_∩)O哈哈
网友评论