Native Module е Java или Objective-C клас, който прави нативните API на платформата достъпни от JavaScript в React Native. Всеки модул се регистрира в Bridge и експортира методи, които могат да бъдат извиквани от JS кода като обикновени функции. Според данните на Meta, 2024, Native Module остава основният начин за интеграция на платформен код в React Native приложения.
Основни моменти
Native Module е архитектурен елемент на React Native, който позволява изпълнение на код на езика на платформата (Objective-C/Swift за iOS, Java/Kotlin за Android) и връщане на резултата в JavaScript. Без Native Module достъпът до нативните възможности на устройството — камера, GPS, акселерометър, файлова система или Bluetooth — е невъзможен.
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 и комуникира с JS асинхронно чрез JSON сериализация. В новата архитектура (React Native 0.76+) модулът може да работи като Turbo Module, използвайки JSI за синхронен достъп без сериализация.
Създаването на Native Module за iOS започва с деклариране на Objective-C клас, който имплементира протокола RCTBridgeModule. Макросът RCT_EXPORT_MODULE регистрира модула в Bridge, а RCT_EXPORT_METHOD експортира метод, достъпен от JavaScript.
// ImageCompressor.m — Native Module за iOS
@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 пред класа и методите, за да бъдат достъпни за Objective-C средата, с която Bridge работи. Класът трябва да наследява 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 се създава като Java клас, наследяващ ReactContextBaseJavaModule. Анотацията @ReactMethod експортира метода в Bridge. За връщане на резултат се използва интерфейсът Promise от com.facebook.react.bridge.
// 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;
}
};
}
След регистрация модулът става достъпен в JavaScript чрез NativeModules. React Native автоматично замества името на модула, посочено в getName() за Android или в RCT_EXPORT_MODULE за iOS.
// Използване на Native Module от JavaScript
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. Вместо Promise могат да се използват функциите за обратно извикване RCTResponseSenderBlock в iOS и Callback в Android. Въпреки това Promise се счита за модерен стандарт и се препоръчва за нови модули.
Native Module се дебъгва като обикновен нативен код — поставете точки на прекъсване в Xcode или Android Studio. За iOS използвайте схема за изграждане с React Native, за Android — Debug конфигурация. Точката на влизане — методи, извикани от JS.
Native Module чрез Bridge не поддържа бинарни данни (NSData/byte[]), персонализирани обекти и функции. За предаване на изображения използвайте път до файл или base64 низ. Turbo Module чрез JSI премахва част от тези ограничения.
Обикновено не — Native Module API е стабилен и обратно съвместим. При преход към нова архитектура (Turbo Module) модулът се адаптира чрез декоратори, но съществуващият код продължава да работи.
Използвайте RCTEventEmitter в iOS или DeviceEventEmitter в Android. Модулът изпраща събитие, а JS страната се абонира чрез NativeEventEmitter от react-native. Това е полезно за поточни данни и събития от сензори.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също