Bridge е архитектурен компонент на React Native, който осигурява асинхронна комуникация между JavaScript нишката и нативната среда на iOS и Android. Той предава сериализирани JSON съобщения чрез опашка, позволявайки извикване на нативни API от JS код. Според данни на Meta, 2024, Bridge остава основата на съществуващите приложения, въпреки че отстъпва по производителност на новата архитектура, базирана на JSI.
Основни точки
Bridge (Мост) — ключов архитектурен елемент на React Native, който осигурява двупосочна асинхронна връзка между JavaScript нишката, където се изпълнява бизнес логиката на приложението, и нативните нишки на iOS и Android. От пускането на React Native през 2015 г. Bridge остава единственият начин за взаимодействие на JS код с платформените API — камера, геолокация, файлова система, известия и други нативни възможности.
Архитектурата на Bridge се основава на принципа на опашка от съобщения (message queue). Когато JavaScript код извика нативен метод, заявката се сериализира в JSON низ, поставя се в опашка и се изпраща асинхронно към нативната страна. Нативният код обработва заявката, изпълнява съответната операция и изпраща резултата обратно чрез същата опашка към JS нишката. Според доклада на Meta на React Conf 2021, през Bridge преминават до 10 000 съобщения в секунда в средностатистическо приложение.
Основните нишки, участващи в работата на Bridge: JavaScript Thread (изпълнение на JS код), Native Thread (изпълнение на нативни операции) и Shadow Thread (изчисляване на оформлението с помощта на Yoga). Всяка нишка работи независимо, което осигурява отзивчивост на интерфейса — нативните анимации не се блокират от JS изчисления.
Bridge използва три ключови механизма за комуникация: MessageQueue, JSON сериализация и групиране на съобщения. MessageQueue е вътрешен компонент на React Native, който управлява опашката от извиквания между JS и нативната страна. Всяко извикване на нативен метод се поставя в опашка, сериализира се и се изпраща на партиди за оптимизация на производителността.
MessageQueue работи на принципа на групиране: извикванията на нативни методи се натрупват и изпращат като една група (batch) на всеки 5–15 милисекунди. Това намалява допълнителното натоварване от сериализация, тъй като няколко извиквания се опаковат в един JSON пакет. От нативната страна съобщенията се десериализират и разпределят към съответните модули.
Нативните модули се регистрират автоматично чрез макроси или анотации. На iOS се използва макросът RCT_EXPORT_MODULE, на Android — анотацията @ReactMethod. React Native сканира регистрираните модули при стартиране на приложението и изгражда конфигурационна JSON карта на всички налични методи. Тази карта се предава на JS средата и JavaScript узнава кои методи могат да бъдат извиквани.
Данните преминават следния път: JavaScript извиква NativeModules.CalendarModule.createCalendarEvent(). Методът се сериализира в JSON съобщение с идентификатор на модула, име на метода и аргументи. Съобщението попада в опашката на MessageQueue. В нативната нишка съобщението се десериализира и се предава на съответния модул. Резултатът от изпълнението се сериализира обратно и се изпраща в JS нишката като Promise или callback.
// Извикване на нативен модул от JavaScript чрез Bridge
import { NativeModules } from 'react-native';
const CalendarModule = NativeModules.CalendarModule;
CalendarModule.createCalendarEvent('Test Event', 'Office')
.then(eventId => {
console.log('Създадено събитие с id:', eventId);
})
.catch(error => {
console.error('Неуспешно:', error);
});
От нативната страна на iOS модулът изглежда като Objective-C клас с макрос RCT_EXPORT_MODULE. Методът се експортира с макрос RCT_EXPORT_METHOD и React Native автоматично го регистрира в Bridge. Аргументите се предават по позиция и трябва да съответстват на поддържаните JSON типове: NSString, NSNumber, NSArray, NSDictionary, BOOL.
// Регистрация на iOS нативен модул в Bridge
@interface CalendarModule () RCT_EXPORT_MODULE()
@end
@implementation CalendarModule
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name
location:(NSString *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSNumber *eventId = createEvent(name, location);
resolve(eventId);
}
@end
Bridge има редица фундаментални ограничения на производителността. Основното от тях е задължителната асинхронност и сериализация. Всяко извикване на нативен метод превръща данните в JSON низ, което добавя закъснение и изразходва памет. За операции с големи обеми данни, като обработка на изображения или работа с видео, това се превръща в тясно място.
JSON сериализацията и десериализацията заемат процесорно време и памет. Всяко съобщение трябва да бъде преобразувано в низ от JS страната, предадено през моста и анализирано от нативната страна. Според тестове на Callstack (2022), сериализацията на масив от 10 000 числа чрез Bridge отнема около 30–50 милисекунди, което е неприемливо за високочестотни извиквания.
Bridge не е оптимизиран за предаване на големи двоични данни. Снимки, аудио файлове и видео потоци изискват алтернативни подходи — например запис на файла на диска и предаване на пътя като низ. Това създава допълнително натоварване за четене и запис на файловата система.
Осъзнаването на тези ограничения доведе екипа на Meta до разработването на нова архитектура за React Native, в която Bridge се заменя с JSI (JavaScript Interface) и Turbo Module. JSI позволява директно извикване на нативни методи без сериализация, което елиминира основния недостатък на Bridge.
Сравнението на Bridge и Turbo Module показва фундаментални разлики в архитектурните подходи. Bridge използва асинхронна опашка от съобщения с JSON сериализация, докато Turbo Module работи чрез JSI — пряк интерфейс между JavaScript и C++, който позволява синхронно извикване на нативни методи без преобразуване на данни.
| Характеристика | Bridge | Turbo Module |
|---|---|---|
| Тип извикване | Асинхронно | Синхронно и асинхронно |
| Сериализация | JSON при всяко извикване | JSI обекти без копиране |
| Производителност | Средна | Висока |
| Типизация | Динамична | Статична (Codegen) |
| Зареждане | Всички модули при стартиране | Мързеливо (при поискване) |
Изборът между Bridge и Turbo Module зависи от версията на React Native. За проекти на React Native 0.72 и по-стари, Bridge остава основният механизъм. От React Native 0.73 нататък, Metro и новата архитектура се поддържат паралелно, позволявайки постепенна миграция. Пълният преход към Turbo Module изисква обновяване до React Native 0.76+ и активиране на новата архитектура в конфигурацията.
Нека разгледаме пълния цикъл на създаване и използване на Native Module чрез Bridge с пример за модул за работа с календар. Модулът ще създава събитие и ще връща неговия идентификатор. Този пример обхваща настройката и за двете платформи — iOS и Android.
На Android, Native Module се създава като Java клас, наследяващ ReactContextBaseJavaModule. Анотацията @ReactMethod експортира метода в Bridge. За Promise се използва интерфейсът Promise от com.facebook.react.bridge.
public class CalendarModule extends ReactContextBaseJavaModule {
@Override
public String getName() {
return "CalendarModule";
}
@ReactMethod
public void createCalendarEvent(
String name,
String location,
Promise promise) {
try {
Integer eventId = createCalendarEventNative(name, location);
promise.resolve(eventId);
} catch (Exception e) {
promise.reject("EVENT_ERROR", e.getMessage());
}
}
}
Модулът се регистрира чрез @ReactModule или ръчно в пакета на приложението. React Native автоматично го открива и го добавя в Bridge. След регистрация модулът е достъпен от JavaScript чрез NativeModules.
public class CalendarPackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
return Arrays.asList(
new CalendarModule(reactContext)
);
}
@Override
public List<ViewManager> createViewManagers(
ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Важно е да се отбележи, че Bridge изисква рестартиране на приложението при добавяне на нови модули, тъй като конфигурационната карта се изгражда веднъж по време на инициализация. Това го отличава от Turbo Module, който се зарежда мързеливо и поддържа горещо презареждане на модули без рестарт.
Често задавани въпроси
Bridge винаги използва асинхронна опашка и JSON сериализация, докато директното предаване чрез JSI работи синхронно и без копиране на данни. Bridge създава закъснение за сериализация, но осигурява изолация на нишките.
Не, Bridge поддържа само асинхронни извиквания. За синхронно взаимодействие е необходима новата архитектура с JSI и Turbo Module. Това е едно от ключовите ограничения, което е решено в React Native 0.76+.
Bridge поддържа типове, сериализируеми в JSON: низове, числа, булеви стойности, масиви, речници (обекти). Двоични данни, като изображения, трябва да се предават чрез файловата система или base64 кодиране.
За измерване използвайте React DevTools и профилиращия инструмент на React Native. Разделът Performance показва броя на съобщенията в опашката на Bridge и закъсненията. Също така е наличен пакетът react-native-bridge-spy за мониторинг на трафика.
Преминаването се препоръчва за проекти, изискващи висока производителност, или при създаване на нови приложения на React Native 0.76+. За съществуващи проекти миграцията може да бъде постепенна — и двете архитектури работят паралелно.
Обобщение
Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също