React Native 中的 Native Module — 什么是它,如何创建和集成

作者: IT Sectr 发布日期: 2026-06-03 阅读时间: 9 分钟

Native Module 是一个 Java 或 Objective-C 类,它使平台的本地 API 可以从 React Native 中的 JavaScript 访问。每个模块在 Bridge 中注册并导出方法,这些方法可以从 JS 代码中像普通函数一样调用。根据 Meta,2024 的数据,Native Module 仍然是 React Native 应用程序中集成平台代码的主要方式。

要点

  • Native Module — JavaScript 与 iOS 或 Android 原生代码之间的桥梁。
  • 注册 — 模块通过 iOS(RCT_EXPORT_MODULE)或 Android(@ReactMethod)注解声明。
  • 数据类型 — 支持基本类型、字符串、数组、字典和 Promise。
  • 使用 — 从 JS 中可以通过 NativeModules.模块名称 访问模块。
  • 新架构 — 在 React Native 0.76+ 中,Native Module 可以通过 JSI 作为 Turbo Module 工作。

什么是 Native Module?

Native Module 是 React Native 的一个架构元素,允许以平台语言(iOS 的 Objective-C/Swift,Android 的 Java/Kotlin)执行代码并将结果返回给 JavaScript。没有 Native Module,就无法访问设备的原生功能 — 摄像头、GPS、加速度计、文件系统或蓝牙。

React Native 附带了一组内置的 Native Module:CameraRollAsyncStorageGeolocationNetInfo 等。但对于特定任务 — 集成第三方 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

为 iOS 创建 Native Module 从声明一个实现 RCTBridgeModule 协议的 Objective-C 类开始。RCT_EXPORT_MODULE 宏在 Bridge 中注册模块,RCT_EXPORT_METHOD 导出一个可从 JavaScript 访问的方法。

objective-c
// 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。

swift
// 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

在 Android 上,Native Module 作为继承 ReactContextBaseJavaModule 的 Java 类创建。@ReactMethod 注解将方法导出到 Bridge。使用 com.facebook.react.bridge 中的 Promise 接口返回结果。

java
// 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 方法的模块列表中。

java
// 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();
    }
}

注册和使用 Native Module

为两个平台创建模块后,必须在 React Native 中注册它们。对于 Android,在 MainApplication.java 的 getPackages() 方法中添加包。对于 iOS,模块通过 RCT_EXPORT_MODULE 宏自动注册,但也可以在 AppDelegate.mm 文件中进行手动注册。

java
// 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 中指定的模块名称。

js
// 从 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 和 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 的逐步更新,社区正朝着完全过渡到新架构的方向发展。

常见问题

在 Native Module 中可以传递 callback 代替 Promise 吗?

是的,Bridge 支持 callback。可以使用 iOS 中的 RCTResponseSenderBlock 和 Android 中的 Callback 回调函数代替 Promise。然而,Promise 被认为是现代标准,推荐用于新模块。

如何在 Xcode 或 Android Studio 中调试 Native Module?

Native Module 像普通原生代码一样调试 — 在 Xcode 或 Android Studio 中设置断点。对于 iOS,使用 React Native 的构建方案;对于 Android,使用 Debug 配置。入口点是 JS 调用的方法。

Native Module 不支持哪些数据类型?

Native Module 通过 Bridge 不支持二进制数据(NSData/byte[])、自定义对象和函数。传输图像时使用文件路径或 base64 字符串。通过 JSI 的 Turbo Module 消除了部分这些限制。

更新 React Native 时需要重写 Native Module 吗?

通常不需要 — Native Module API 稳定且向后兼容。在过渡到新架构(Turbo Module)时,模块通过装饰器进行适配,但现有代码继续运行。

如何从 Native Module 向 JavaScript 发送事件?

使用 iOS 中的 RCTEventEmitter 或 Android 中的 DeviceEventEmitter。模块发送事件,JS 端通过 react-native 的 NativeEventEmitter 订阅。这对于流数据和传感器事件很有用。

总结

  • Native Module — 使本地 API 可从 React Native 中的 JavaScript 访问的 Java/Objective-C 类。
  • 注册 — iOS 使用 RCT_EXPORT_MODULE,Android 使用带有 @ReactMethod 的 ReactContextBaseJavaModule。
  • 调用 — 从 JS 中可以通过 NativeModules.模块访问模块,支持 Promise 和 callback。
  • 数据类型 — Bridge 仅传输 JSON 兼容的类型:字符串、数字、数组、字典。
  • 性能 — 经典 Native Module 由于异步和序列化而逊于 Turbo Module。
  • 迁移 — 在过渡到 React Native 0.76+ 时,模块可以无需重写逻辑地作为 Turbo Module 工作。
  • 应用 — Native Module 对于摄像头、蓝牙、文件系统和 SDK 集成是不可或缺的。

我们将开发一款交钥匙移动应用程序

IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。

讨论项目

另请阅读