Native Module 是一个 Java 或 Objective-C 类,它使平台的本地 API 可以从 React Native 中的 JavaScript 访问。每个模块在 Bridge 中注册并导出方法,这些方法可以从 JS 代码中像普通函数一样调用。根据 Meta,2024 的数据,Native Module 仍然是 React Native 应用程序中集成平台代码的主要方式。
要点
Native Module 是 React Native 的一个架构元素,允许以平台语言(iOS 的 Objective-C/Swift,Android 的 Java/Kotlin)执行代码并将结果返回给 JavaScript。没有 Native Module,就无法访问设备的原生功能 — 摄像头、GPS、加速度计、文件系统或蓝牙。
React Native 附带了一组内置的 Native Module:CameraRoll、AsyncStorage、Geolocation、NetInfo 等。但对于特定任务 — 集成第三方 SDK、使用硬件传感器或后台进程 — 开发者需要创建自己的模块。根据 State of React Native 2024 调查,67% 的开发者在他们的项目中使用至少一个自定义 Native Module。
Native Module 的架构取决于 React Native 的版本。在经典架构(React Native 0.72 及更早版本)中,模块通过 Bridge 连接并通过 JSON 序列化与 JS 异步通信。在新架构(React Native 0.76+)中,模块可以作为 Turbo Module 工作,使用 JSI 进行无需序列化的同步访问。
为 iOS 创建 Native Module 从声明一个实现 RCTBridgeModule 协议的 Objective-C 类开始。RCT_EXPORT_MODULE 宏在 Bridge 中注册模块,RCT_EXPORT_METHOD 导出一个可从 JavaScript 访问的方法。
// ImageCompressor.m — 为 iOS 的 Native Module
@interface ImageCompressor () RCT_EXPORT_MODULE()
@end
@implementation ImageCompressor
RCT_EXPORT_METHOD(compressImage:(NSString *)imagePath
quality:(NSNumber *)quality
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
NSData *compressedData = [UIImageJPEGRepresentation(image, quality.floatValue)];
NSString *outputPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"compressed.jpg"];
[compressedData writeToFile:outputPath atomically:YES];
resolve(outputPath);
}
@end
compressImage 方法接收图像路径和压缩质量(0.0–1.0),在原生端处理数据并返回压缩文件的路径。关键优势 — 压缩由原生代码执行,这比 JavaScript 中的类似操作更快且更节省内存。
对于 Swift 模块,在类和方法之前使用 @objc 注解,以便它们对 Bridge 工作的 Objective-C 运行时可见。该类必须继承 NSObject 并实现 RCTBridgeModule。
// ImageCompressor.swift — Swift Native Module
@objc(ImageCompressor)
class ImageCompressor: NSObject {
@objc
func compressImage(
_ imagePath: String,
quality: Float,
resolver: @escaping RCTPromiseResolveBlock,
rejecter: @escaping RCTPromiseRejectBlock
) {
guard let image = UIImage(contentsOfFile: imagePath) else {
rejecter("FILE_ERROR", "Cannot load image", nil)
return
}
guard let data = image.jpegData(compressionQuality: CGFloat(quality)) else {
rejecter("COMPRESS_ERROR", "Compression failed", nil)
return
}
let outputPath = NSTemporaryDirectory() + "compressed.jpg"
try? data.write(to: URL(fileURLWithPath: outputPath))
resolver(outputPath)
}
}
在 Android 上,Native Module 作为继承 ReactContextBaseJavaModule 的 Java 类创建。@ReactMethod 注解将方法导出到 Bridge。使用 com.facebook.react.bridge 中的 Promise 接口返回结果。
// ImageCompressorModule.java — Android Native Module
public class ImageCompressorModule
extends ReactContextBaseJavaModule {
@Override
public String getName() {
return "ImageCompressor";
}
@ReactMethod
public void compressImage(
String imagePath,
Float quality,
Promise promise) {
try {
Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
File outputFile = new File(
ReactNative.getApplicationContext()
.getCacheDir(), "compressed.jpg");
FileOutputStream fos = new FileOutputStream(outputFile);
bitmap.compress(
Bitmap.CompressFormat.JPEG,
(int)(quality * 100), fos);
fos.close();
promise.resolve(outputFile.getAbsolutePath());
} catch (Exception e) {
promise.reject("COMPRESS_ERROR", e.getMessage());
}
}
}
getName() 方法返回模块的名称,模块将在该名称下从 JavaScript 访问。在示例中,模块注册为 ImageCompressor。@ReactMethod 注解指示 Bridge 该方法应该被导出。重要提示:方法必须是 void 并且只接受 Bridge 支持的类型:String、Boolean、Integer、Double、ReadableArray、ReadableMap、Promise。
创建类后,模块必须在应用程序包中注册。为此,创建一个实现 ReactPackage 的类,并将其添加到 createNativeModules 方法的模块列表中。
// ImageCompressorPackage.java — 模块注册
public class ImageCompressorPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.asList(
new ImageCompressorModule(reactContext)
);
}
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
为两个平台创建模块后,必须在 React Native 中注册它们。对于 Android,在 MainApplication.java 的 getPackages() 方法中添加包。对于 iOS,模块通过 RCT_EXPORT_MODULE 宏自动注册,但也可以在 AppDelegate.mm 文件中进行手动注册。
// MainApplication.java — 将包添加到 React Native
import com.yourapp.nativemodules.ImageCompressorPackage;
public class MainApplication extends Application
implements ReactApplication {
private final ReactNativeHost mReactNativeHost =
new ReactNativeHost(this) {
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages =
new PackageList(this).getPackages();
packages.add(new ImageCompressorPackage());
return packages;
}
};
}
注册后,模块可以通过 NativeModules 在 JavaScript 中访问。React Native 自动替换在 Android 的 getName() 或 iOS 的 RCT_EXPORT_MODULE 中指定的模块名称。
// 从 JavaScript 使用 Native Module
import { NativeModules } from 'react-native';
import { Platform } from 'react-native';
const ImageCompressor = NativeModules.ImageCompressor;
async function compressPhoto(uri: string) {
try {
const result = await ImageCompressor.compressImage(
uri.replace('file://', ''), 0.8
);
console.log('已压缩:', result);
return result;
} catch (error) {
console.error('压缩失败:', error);
throw error;
}
}
经典 Native Module 和 Turbo Module 的比较有助于理解为新项目选择哪种方法。两种机制都提供对原生代码的访问,但在架构和性能上有根本区别。
| 特性 | Native Module(Bridge) | Turbo Module(JSI) |
|---|---|---|
| 通信 | 通过 JSON 异步 | 通过 JSI 同步 |
| 序列化 | 每次调用都使用 JSON | 无需数据复制 |
| 类型化 | 手动,无生成 | 通过 Codegen 自动 |
| 加载 | 在应用程序初始化时 | 懒加载(lazy load) |
| 兼容性 | 所有 React Native 版本 | React Native 0.73+ |
对于 React Native 0.72 及更早版本上的现有项目,经典 Native Module 仍然是主要选择。对于新项目,建议使用 Turbo Module,特别是在频繁调用原生方法时需要高性能的情况下。随着 React Native 的逐步更新,社区正朝着完全过渡到新架构的方向发展。
常见问题
是的,Bridge 支持 callback。可以使用 iOS 中的 RCTResponseSenderBlock 和 Android 中的 Callback 回调函数代替 Promise。然而,Promise 被认为是现代标准,推荐用于新模块。
Native Module 像普通原生代码一样调试 — 在 Xcode 或 Android Studio 中设置断点。对于 iOS,使用 React Native 的构建方案;对于 Android,使用 Debug 配置。入口点是 JS 调用的方法。
Native Module 通过 Bridge 不支持二进制数据(NSData/byte[])、自定义对象和函数。传输图像时使用文件路径或 base64 字符串。通过 JSI 的 Turbo Module 消除了部分这些限制。
通常不需要 — Native Module API 稳定且向后兼容。在过渡到新架构(Turbo Module)时,模块通过装饰器进行适配,但现有代码继续运行。
使用 iOS 中的 RCTEventEmitter 或 Android 中的 DeviceEventEmitter。模块发送事件,JS 端通过 react-native 的 NativeEventEmitter 订阅。这对于流数据和传感器事件很有用。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。