React Native自定義原生(iOS和Android)模塊Module(以控制手電筒手電筒為例)

BG:為什么需要封裝原生module?

引用官網(wǎng)介紹:

有時候 App 需要訪問平臺 API,但 React Native 可能還沒有相應(yīng)的模塊封裝;或者你需要復(fù)用 Objective-C、Swift 或 C++代碼,而不是用 JavaScript 重新實現(xiàn)一遍;又或者你需要實現(xiàn)某些高性能、多線程的代碼,譬如圖片處理、數(shù)據(jù)庫、或者各種高級擴展等等。

React Native 的高級的特性:可以在其基礎(chǔ)上編寫真正的原生代碼,并且可以訪問平臺所有的能力。這是一個相對高級的特性,我們并不認(rèn)為它應(yīng)當(dāng)在日常開發(fā)的過程中經(jīng)常出現(xiàn),但具備這樣的能力是很重要的。如果 React Native 還不支持某個你需要的原生特性,你應(yīng)當(dāng)可以自己實現(xiàn)該特性的封裝。

總結(jié)簡單一句話就是:如果 React Native 不能滿足你,那你就去自定義module。

閑話少說,開始實戰(zhàn),下面我們以實現(xiàn)調(diào)用原生的手電筒開關(guān)功能為例,此例子將展現(xiàn)RN和原生的雙向通信:

iOS平臺:

在 React Native 中,一個“iOS原生模塊”就是一個實現(xiàn)了“RCTBridgeModule”協(xié)議的 Objective-C 類,其中 RCT 是 ReaCT 的縮寫。
類名我們暫定為CameraControlUtil。
1.定義頭文件

#import <Foundation/Foundation.h>
#import <React/RCTBridgeModule.h>
@interface CameraControlUtil : NSObject<RCTBridgeModule>

@end

為了實現(xiàn)RCTBridgeModule協(xié)議,你的類需要包含RCT_EXPORT_MODULE()宏。這個宏也可以添加一個參數(shù)用來指定在 JavaScript 中訪問這個模塊的名字。如果你不指定,默認(rèn)就會使用這個 Objective-C 類的名字。如果類名以 RCT 開頭,則 JavaScript 端引入的模塊名會自動移除這個前綴。

2.模塊導(dǎo)出

#import "CameraControlUtil.h"
#import <AVFoundation/AVFoundation.h>
@interface CameraControlUtil ()
@property (nonatomic,assign) BOOL lightOn;//手電筒狀態(tài),true:開啟,false:關(guān)閉
@end

@implementation CameraControlUtil
// 導(dǎo)出模塊,不添加參數(shù)即默認(rèn)為這個類名
RCT_EXPORT_MODULE();
@end

3.方法定義

你必須明確的聲明要給 JavaScript 導(dǎo)出的方法,否則 React Native 不會導(dǎo)出任何方法。聲明通過RCT_EXPORT_METHOD()宏來實現(xiàn):

這里我們定義方法名:devicesetTorchOn;
入?yún)ⅲ?br> state(控制手電筒開關(guān),true:打開,false:關(guān)閉);
成功block回調(diào):successCallback(是否成功,true:成功,false:失敗);
失敗block回調(diào):failCallback(失敗回調(diào))

注意:1.JavaScript 方法名
導(dǎo)出到 JavaScript 的方法名是 Objective-C 的方法名的第一個部分。React Native 還定義了一個RCT_REMAP_METHOD()宏,它可以指定 JavaScript 方法名。因為 JavaScript 端不能有同名不同參的方法存在,所以當(dāng)原生端存在重載方法時,可以使用這個宏來避免在 JavaScript 端的名字沖突。

注意:2.返回值類型必須是void
2.橋接到 JavaScript 的方法返回值類型必須是void。React Native 的橋接操作是異步的,所以要返回結(jié)果給 JavaScript,你必須通過回調(diào)或者觸發(fā)事件來進行

/**
 操作手電筒方法
 @param state 控制手電筒開關(guān),true:打開,false:關(guān)閉
 @param 操作結(jié)果的回調(diào)successCallback:是否成功,failCallback:失敗回調(diào))
 */
RCT_EXPORT_METHOD(devicesetTorchOn:(BOOL)state resolver:(RCTResponseSenderBlock)successCallback resolver:(RCTResponseSenderBlock)failCallback){
  NSString *callbackData = @"操作成功";
  BOOL isSucc = YES;
  _lightOn = !_lightOn;//可以定義一個變量記錄手電筒的狀態(tài),也可以根據(jù)自己需要使用入?yún)⒌膕tate控制手電筒開閉。
  Class captureDeviceClass = NSClassFromString(@"AVCaptureDevice");
  if(captureDeviceClass !=nil) {
  AVCaptureDevice*device = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];
     if([device hasTorch]) { // 判斷是否有手電筒
      // 請求獨占訪問硬件設(shè)備
      [device lockForConfiguration:nil];
        if(_lightOn) {
          [device setTorchMode:AVCaptureTorchModeOn];//手電筒開
        }else{
          [device setTorchMode:AVCaptureTorchModeOff]; // 手電筒關(guān)
          }
      // 請求解除獨占訪問硬件設(shè)備
       [device unlockForConfiguration];
     }else{
       isSucc = NO;
       callbackData = @"手電筒不可用!";
     }
  }else{
       isSucc = NO;
     callbackData = @"手電筒不可用!";
  }
   //準(zhǔn)備回調(diào)回去的數(shù)據(jù)
  successCallback(@[[[NSNumber alloc]initWithBool:isSucc]]);
//  callback(@[[[NSNumber alloc]initWithBool:isSucc],callbackData]);
  failCallback(@[callbackData]);
}

如果只想寫一個回調(diào),可以通過數(shù)組組裝自己的任意回調(diào),比如:

callback(@[[[NSNumber alloc]initWithBool:isSucc],callbackData]);

4.Javascript 中進行調(diào)用

***導(dǎo)入模塊寫法一:
import {
    NativeModules
} from 'react-native';

***導(dǎo)入模塊寫法二:
var CameraControlUtil = require('react-native').NativeModules.CameraControlUtil;
***devicesetTorchOn方法調(diào)用:
//在按鈕事件onPressConfirmBtn中調(diào)用
onPressConfirmBtn = (open)=>{
      NativeModules.CameraControlUtil.devicesetTorchOn((!this.state.flashlightPress),(success) => {
          //操作成功
          Utils.consoleLog(`打開手電筒成功--${success}`);
         ...//你的成功處理
    },(errorMsg) =>{
        //操作失敗,你的失敗處理,可以彈框提示
      Utils.consoleLog(`打開手電筒errorMsg--${errorMsg}`);
      Alert.alert(
        '提示',
        errorMsg,
        [
          {text: '我知道了' onPress: () => Utils.consoleLog('操作結(jié)果alert--press i know')}
        ])
    })
    }

Android平臺:

1.創(chuàng)建模塊
本著開發(fā)維護方便,在實現(xiàn)某個原生module的時候,我們最好在iOS平臺和Android平臺上定義相同的module名字以及method名字,最好連回調(diào)的數(shù)據(jù)也同一格式,這樣js調(diào)用的時候不同平臺就不用作不同的處理了。當(dāng)然,某些特定的情況下,我們也需要提供不同的方法及回調(diào)~~~
我們在app的java文件夾下創(chuàng)建一個ModuleForRN文件夾,同一管理整個app中需要的原生module。我們同樣創(chuàng)建一個名為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{
    ...模塊導(dǎo)出、模塊實現(xiàn)
}

2.模塊實現(xiàn)
2.1構(gòu)建方法

  private Camera camera;
  private Boolean isLightOn = false;//記錄手電筒狀態(tài)
  private final ReactApplicationContext myReactContext;
//構(gòu)建方法
public CameraControlUtil(ReactApplicationContext reactContext) {
        super(reactContext);
        this.myReactContext = reactContext;
    }

2.2模塊名稱

 /**
     * 返回一個模塊名稱,rn通過NativeModules可以調(diào)用此模塊
     */
    @Override
    public String getName() {
        return "CameraControlUtil";
    }

2.3模塊實現(xiàn)

/**
     * @param state 控制手電筒開關(guān),true:打開,false:關(guān)閉
     * @param successCallback 打開成功的回調(diào)
     * @param failCallback 打開失敗的回調(diào)
     */
    @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,實現(xiàn)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));//打開設(shè)置頁面
        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 中進行調(diào)用
由于我們在Android和iOS平臺下的module名字和method名字及回調(diào)都一樣,因此RN調(diào)用時,就跟iOS的調(diào)用一模一樣了,9??不再寫一遍啦~~~~??

以上就是React Native自定義module的介紹了,希望對你有幫助,如有歡迎大家指正錯誤,歡迎大家留言交流~~O(∩_∩)O哈哈

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容