Native Module är en Java- eller Objective-C-klass som gör plattformens inbyggda API:er tillgängliga från JavaScript i React Native. Varje modul registreras i Bridge och exporterar metoder som kan anropas från JS-kod som vanliga funktioner. Enligt Meta, 2024 förblir Native Module det huvudsakliga sättet att integrera plattformskod i React Native-applikationer.
Huvudpunkter
Native Module är en arkitektonisk komponent i React Native som gör det möjligt att utföra kod på plattformens språk (Objective-C/Swift för iOS, Java/Kotlin för Android) och returnera resultatet till JavaScript. Utan Native Module är åtkomst till enhetens inbyggda funktioner — kamera, GPS, accelerometer, filsystem eller Bluetooth — omöjlig.
React Native levereras med en uppsättning inbyggda Native Module: CameraRoll, AsyncStorage, Geolocation, NetInfo och andra. Men för specifika uppgifter — integration av tredjeparts-SDK, arbete med hårdvarusensorer eller bakgrundsprocesser — skapar utvecklaren egna moduler. Enligt State of React Native 2024-undersökningen använder 67 % av utvecklarna minst en anpassad Native Module i sina projekt.
Arkitekturen för Native Module beror på React Native-versionen. I den klassiska arkitekturen (React Native 0.72 och äldre) ansluts modulen via Bridge och kommunicerar med JS asynkront via JSON-serialisering. I den nya arkitekturen (React Native 0.76+) kan modulen fungera som Turbo Module och använda JSI för synkron åtkomst utan serialisering.
Att skapa en Native Module för iOS börjar med att deklarera en Objective-C-klass som implementerar protokollet RCTBridgeModule. Makrot RCT_EXPORT_MODULE registrerar modulen i Bridge och RCT_EXPORT_METHOD exporterar en metod som är tillgänglig från JavaScript.
// ImageCompressor.m — Native Module för 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
Metoden compressImage tar emot sökvägen till bilden och komprimeringskvaliteten (0.0–1.0), bearbetar data på den inbyggda sidan och returnerar sökvägen till den komprimerade filen. Den viktigaste fördelen — komprimering utförs med inbyggd kod, vilket är betydligt snabbare och mer minneseffektivt än en liknande operation i JavaScript.
För Swift-moduler används annoteringen @objc före klassen och metoderna för att de ska vara tillgängliga för Objective-C-körtiden som Bridge arbetar med. Klassen måste ärva från NSObject och implementera 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)
}
}
På Android skapas Native Module som en Java-klass som ärver från ReactContextBaseJavaModule. Annoteringen @ReactMethod exporterar metoden till Bridge. För att returnera resultatet används Promise-gränssnittet från 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());
}
}
}
Metoden getName() returnerar namnet på modulen under vilken den kommer att vara tillgänglig från JavaScript. I det givna exemplet registreras modulen som ImageCompressor. Annoteringen @ReactMethod indikerar för Bridge att metoden ska exporteras. Viktigt: metoder måste vara void och endast acceptera typer som stöds av Bridge: String, Boolean, Integer, Double, ReadableArray, ReadableMap, Promise.
Efter att klassen skapats måste modulen registreras i applikationspaketet. För detta skapas en klass som implementerar ReactPackage och läggs till i listan över moduler i metoden createNativeModules.
// ImageCompressorPackage.java — modulregistrering
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();
}
}
Efter att moduler skapats för båda plattformarna måste de registreras i React Native. För Android läggs paketet till i MainApplication.java i metoden getPackages(). För iOS registreras modulen automatiskt via makrot RCT_EXPORT_MODULE, men manuell registrering i filen AppDelegate.mm kan också användas.
// MainApplication.java — lägga till paket i 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;
}
};
}
Efter registrering blir modulen tillgänglig i JavaScript via NativeModules. React Native ersätter automatiskt modulnamnet som anges i getName() för Android eller i RCT_EXPORT_MODULE för iOS.
// Använda Native Module från 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('Komprimerad:', result);
return result;
} catch (error) {
console.error('Komprimering misslyckades:', error);
throw error;
}
}
En jämförelse av klassisk Native Module och Turbo Module hjälper till att förstå vilket tillvägagångssätt som ska väljas för ett nytt projekt. Båda mekanismerna ger tillgång till inbyggd kod, men skiljer sig fundamentalt i arkitektur och prestanda.
| Egenskap | Native Module (Bridge) | Turbo Module (JSI) |
|---|---|---|
| Kommunikation | Asynkron via JSON | Synkron via JSI |
| Serialisering | JSON vid varje anrop | Utan datakopiering |
| Typning | Manuell, utan generering | Automatisk via Codegen |
| Laddning | Vid applikationsinitiering | Lazy load |
| Kompatibilitet | Alla React Native-versioner | React Native 0.73+ |
För befintliga projekt på React Native 0.72 och äldre förblir klassisk Native Module det främsta valet. För nya projekt rekommenderas användning av Turbo Module, särskilt om hög prestanda krävs vid frekventa anrop av inbyggda metoder. Med den gradvisa uppdateringen av React Native rör sig communityn mot en fullständig övergång till den nya arkitekturen.
Vanliga frågor
Ja, Bridge stöder callback. Istället för Promise kan återanropsfunktionerna RCTResponseSenderBlock i iOS och Callback i Android användas. Promise anses dock vara en modern standard och rekommenderas för nya moduler.
Native Module felsöks som vanlig inbyggd kod — sätt brytpunkter i Xcode eller Android Studio. För iOS använd byggschemat med React Native, för Android — Debug-konfiguration. Ingångspunkten är metoder som anropas från JS.
Native Module via Bridge stöder inte binär data (NSData/byte[]), anpassade objekt och funktioner. För överföring av bilder använd filsökväg eller base64-sträng. Turbo Module via JSI tar bort en del av dessa begränsningar.
Vanligtvis inte — Native Module API är stabilt och bakåtkompatibelt. Vid övergång till ny arkitektur (Turbo Module) anpassas modulen via dekoratörer, men befintlig kod fortsätter att fungera.
Använd RCTEventEmitter i iOS eller DeviceEventEmitter i Android. Modulen skickar händelsen och JS-sidan prenumererar via NativeEventEmitter från react-native. Detta är användbart för strömmande data och sensorhändelser.
Sammanfattning
Vi utvecklar en mobil applikation nyckelfärdigt
IT Sectr skapar iOS- och Android-applikationer för startups och företag sedan 2017. Vi ger dig råd och föreslår den bästa lösningen.
Läs också