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). Кожен потік працює незалежно, що забезпечує чутливість UI — нативні анімації не блокуються JS-обчисленнями.
Bridge використовує три ключові механізми для комунікації: MessageQueue, серіалізацію JSON та пакетування повідомлень (batching). MessageQueue — це внутрішній компонент React Native, який керує чергою викликів між JS та нативним боком. Кожен виклик нативного методу поміщується в чергу, серіалізується та відправляється пакетами для оптимізації продуктивності.
MessageQueue працює за принципом пакетування: виклики нативних методів накопичуються та відправляються як одна група (пакет) кожні 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('Створено подію з ід:', 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 споживають час CPU та пам’ять. Кожне повідомлення має бути перетворене в рядок на 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також