美文网首页React Native开发React Native实践React-Native 开发阵营
React Native自定义原生(iOS和Android)模块

React Native自定义原生(iOS和Android)模块

作者: 星辰大海_王 | 来源:发表于2018-12-23 18:05 被阅读14次

    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哈哈

    相关文章

      网友评论

        本文标题:React Native自定义原生(iOS和Android)模块

        本文链接:https://www.haomeiwen.com/subject/bwdnkqtx.html