Skip to main content

插件开发

React Native 原生插件开发全解析

React Native 原生插件开发是连接 JavaScript 与原生平台(Android/iOS)的关键技术,能够让你利用原生 API 实现高性能功能。以下从核心概念到实战步骤全面解析原生插件开发。

一、核心概念与架构

1. 插件架构

React Native 原生插件采用双向通信架构

  • JavaScript 层:通过 NativeModules 调用原生功能。
  • 桥接层:CatalystInstance 负责 JS 与原生的消息传递。
  • 原生层:实现具体平台功能(Java/Kotlin 或 Objective-C/Swift)。

2. 通信机制

  • 异步调用:大多数原生方法通过消息队列异步调用。
  • 同步调用:通过 TurboModules 支持同步调用(仅限新架构)。

二、Android 原生插件开发

1. 创建原生模块

// MyNativeModule.java
package com.myplugin;

import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.Promise;

public class MyNativeModule extends ReactContextBaseJavaModule {
private ReactApplicationContext reactContext;

public MyNativeModule(ReactApplicationContext context) {
super(context);
this.reactContext = context;
}

@Override
public String getName() {
return "MyNativeModule"; // JS 中通过 NativeModules.MyNativeModule 访问
}

// 异步方法(带 Promise)
@ReactMethod
public void getDeviceInfo(Promise promise) {
try {
WritableMap result = Arguments.createMap();
result.putString("model", Build.MODEL);
result.putString("version", Build.VERSION.RELEASE);
promise.resolve(result);
} catch (Exception e) {
promise.reject("ERROR", e.getMessage());
}
}

// 同步方法(仅限新架构 TurboModules)
@ReactMethod(isBlockingSynchronousMethod = true)
public String getAppName() {
return reactContext.getPackageName();
}
}

2. 创建包(Package)

// MyPackage.java
package com.myplugin;

import com.facebook.react.ReactPackage;
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 MyPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new MyNativeModule(reactContext));
return modules;
}

@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList(); // 若有自定义视图管理器,在此添加
}
}

3. 注册包

// MainApplication.java
@Override
protected List<ReactPackage> getPackages() {
return Arrays.asList(
new MainReactPackage(),
new MyPackage() // 注册自定义包
);
}

三、iOS 原生插件开发

1. 创建 Objective-C 模块

// RNMyNativeModule.h
#import <React/RCTBridgeModule.h>

@interface RNMyNativeModule : NSObject <RCTBridgeModule>
@end

// RNMyNativeModule.m
#import "RNMyNativeModule.h"

@implementation RNMyNativeModule

RCT_EXPORT_MODULE(MyNativeModule); // JS 中通过 NativeModules.MyNativeModule 访问

// 异步方法
RCT_EXPORT_METHOD(getDeviceInfo:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
NSString *model = [UIDevice currentDevice].model;
NSString *version = [UIDevice currentDevice].systemVersion;

resolve(@{@"model": model, @"version": version});
}

// 同步方法(仅限新架构)
RCT_REMAP_METHOD(getAppName,
getAppNameWithResolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject) {
resolve([[NSBundle mainBundle] bundleIdentifier]);
}

@end

2. Swift 模块开发

// RNMyNativeModule.swift
import Foundation
import React

@objc(RNMyNativeModule)
class RNMyNativeModule: NSObject, RCTBridgeModule {
static func moduleName() -> String! {
return "MyNativeModule"
}

static func requiresMainQueueSetup() -> Bool {
return true
}

@objc func getDeviceInfo(_ resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
let device = UIDevice.current
let info = [
"model": device.model,
"version": device.systemVersion
]
resolve(info)
}

@objc func getAppName(_ resolve: @escaping RCTPromiseResolveBlock,
rejecter reject: @escaping RCTPromiseRejectBlock) {
resolve(Bundle.main.bundleIdentifier)
}
}

四、JavaScript 接口封装

创建 JavaScript 包装层,简化原生模块调用:

// MyNativeModule.js
import { NativeModules, NativeEventEmitter } from 'react-native';

const { MyNativeModule } = NativeModules;

// 确保模块存在
if (!MyNativeModule) {
console.warn('MyNativeModule 未正确加载');
}

export const getDeviceInfo = async () => {
if (!MyNativeModule) return null;
return MyNativeModule.getDeviceInfo();
};

export const getAppName = () => {
if (!MyNativeModule) return null;
return MyNativeModule.getAppName();
};

// 事件监听(如果原生模块发送事件)
const eventEmitter = new NativeEventEmitter(MyNativeModule);
export const addListener = (eventName, callback) => {
return eventEmitter.addListener(eventName, callback);
};

五、自定义视图组件开发

1. Android 端视图管理器

// MyCustomViewManager.java
public class MyCustomViewManager extends SimpleViewManager<MyCustomView> {
public static final String REACT_CLASS = "MyCustomView";

@Override
public String getName() {
return REACT_CLASS;
}

@Override
protected MyCustomView createViewInstance(ThemedReactContext reactContext) {
return new MyCustomView(reactContext);
}

// 设置属性
@ReactProp(name = "color")
public void setColor(MyCustomView view, @Nullable String color) {
view.setColor(color);
}
}

2. iOS 端视图管理器

// RNMyCustomViewManager.h
#import <React/RCTViewManager.h>

@interface RNMyCustomViewManager : RCTViewManager
@end

// RNMyCustomViewManager.m
#import "RNMyCustomViewManager.h"
#import "MyCustomView.h"

@implementation RNMyCustomViewManager

RCT_EXPORT_MODULE(MyCustomView);

- (UIView *)view {
return [[MyCustomView alloc] init];
}

RCT_EXPORT_VIEW_PROPERTY(color, UIColor)

@end

3. JavaScript 组件封装

// MyCustomView.js
import { requireNativeComponent } from 'react-native';

const MyCustomView = requireNativeComponent('MyCustomView');
export default MyCustomView;

六、调试与发布

1. 调试技巧

  • Android:使用 Android Studio 断点调试原生代码。
  • iOS:在 Xcode 中调试 Swift/Objective-C 代码。
  • JS 与原生通信:通过 Chrome DevTools 监控消息队列。

2. 发布到 npm

  1. 创建 package.json 定义插件信息。
  2. 添加文档说明使用方法。
  3. 发布到 npm:
    npm publish

七、新架构(Fabric & TurboModules)适配

1. TurboModules 改造

// TurboModule 实现(Android)
public class MyTurboModule extends ReactContextBaseJavaModule implements TurboModule {
// 实现方法
}

2. Fabric 视图组件

// Fabric 视图组件(Android)
public class MyFabricViewManager extends ViewManager<MyView, MyShadowNode> {
// 实现 Fabric 相关方法
}

八、注意事项

  1. 线程安全

    • 避免在 UI 线程执行耗时操作。
    • 使用 runOnUiThread(Android)或 DispatchQueue(iOS)切换线程。
  2. 内存管理

    • 避免内存泄漏,及时释放资源。
    • onCatalystInstanceDestroy() 中清理资源。
  3. 版本兼容

    • 测试不同 React Native 版本的兼容性。
    • 使用条件编译处理版本差异。

总结

原生插件开发是 React Native 进阶必备技能,通过它可以:

  1. 实现高性能的原生功能(如摄像头、蓝牙)。
  2. 复用已有原生代码库。
  3. 优化关键性能瓶颈。