BG:為什么需要封裝原生module?
有時候 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哈哈