Bridge é um componente arquitetônico do React Native que fornece comunicação assíncrona entre a thread JavaScript e o ambiente nativo do iOS e Android. Ele transmite mensagens JSON serializadas através de uma fila, permitindo chamar APIs nativas a partir do código JS. De acordo com Meta, 2024, Bridge continua sendo a base dos aplicativos existentes, embora perca em desempenho para a nova arquitetura baseada em JSI.
Principais pontos
Bridge é um elemento arquitetônico chave do React Native que fornece comunicação assíncrona bidirecional entre a thread JavaScript, onde a lógica de negócios do aplicativo é executada, e as threads nativas do iOS e Android. Desde o lançamento do React Native em 2015, Bridge tem sido a única maneira do código JS interagir com as APIs da plataforma — câmera, geolocalização, sistema de arquivos, notificações e outras capacidades nativas.
A arquitetura Bridge é baseada no princípio de fila de mensagens (message queue). Quando o código JavaScript chama um método nativo, a solicitação é serializada em uma string JSON, colocada em uma fila e enviada assincronamente para o lado nativo. O código nativo processa a solicitação, executa a operação correspondente e envia o resultado de volta através da mesma fila para a thread JS. De acordo com o relatório da Meta na React Conf 2021, até 10.000 mensagens por segundo passam pelo Bridge em um aplicativo médio.
As principais threads envolvidas na operação do Bridge: JavaScript Thread (execução do código JS), Native Thread (execução de operações nativas) e Shadow Thread (cálculo do layout usando Yoga). Cada thread trabalha de forma independente, garantindo a responsividade da IU — as animações nativas não são bloqueadas pelos cálculos JS.
Bridge usa três mecanismos chave para comunicação: MessageQueue, serialização JSON e agrupamento de mensagens (batching). MessageQueue é um componente interno do React Native que gerencia a fila de chamadas entre JS e o lado nativo. Cada chamada de método nativo é colocada em uma fila, serializada e enviada em lotes para otimização de desempenho.
MessageQueue opera no princípio de agrupamento: as chamadas de métodos nativos se acumulam e são enviadas como um único grupo (lote) a cada 5–15 milissegundos. Isso reduz a sobrecarga de serialização, já que várias chamadas são empacotadas em um único pacote JSON. No lado nativo, as mensagens são desserializadas e distribuídas para os módulos correspondentes.
Os módulos nativos são registrados automaticamente através de macros ou anotações. O iOS usa a macro RCT_EXPORT_MODULE, o Android usa a anotação @ReactMethod. O React Native escaneia os módulos registrados na inicialização do aplicativo e constrói um mapa JSON de configuração de todos os métodos disponíveis. Este mapa é passado para o ambiente JS, e o JavaScript descobre quais métodos podem ser chamados.
Os dados seguem este caminho: JavaScript chama NativeModules.CalendarModule.createCalendarEvent(). O método é serializado em uma mensagem JSON com o identificador do módulo, nome do método e argumentos. A mensagem entra no MessageQueue. Na thread nativa, a mensagem é desserializada e passada para o módulo correspondente. O resultado da execução é serializado de volta e enviado para a thread JS como uma Promise ou callback.
// Native module call from JavaScript via Bridge
import { NativeModules } from 'react-native';
const CalendarModule = NativeModules.CalendarModule;
CalendarModule.createCalendarEvent('Test Event', 'Office')
.then(eventId => {
console.log('Created event with id:', eventId);
})
.catch(error => {
console.error('Failed:', error);
});
No lado nativo do iOS, o módulo parece uma classe Objective-C com a macro RCT_EXPORT_MODULE. O método é exportado usando a macro RCT_EXPORT_METHOD, e o React Native o registra automaticamente no Bridge. Os argumentos são passados por posição e devem corresponder aos tipos JSON suportados: NSString, NSNumber, NSArray, NSDictionary, BOOL.
// iOS Native Module registration in 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 tem uma série de limitações fundamentais de desempenho. A principal é a assincronia e serialização obrigatórias. Cada chamada de método nativo converte dados em uma string JSON, o que adiciona latência e consome memória. Para operações com grandes volumes de dados, como processamento de imagens ou trabalho com vídeo, isso se torna um gargalo.
A serialização e desserialização JSON consomem tempo de CPU e memória. Cada mensagem deve ser convertida em uma string no lado JS, transmitida através da ponte e analisada no lado nativo. De acordo com os testes da Callstack (2022), serializar um array de 10.000 números através do Bridge leva cerca de 30–50 milissegundos, o que é inaceitável para chamadas de alta frequência.
Bridge não é otimizado para transferir grandes dados binários. Fotos, arquivos de áudio e fluxos de vídeo exigem abordagens alternativas — por exemplo, escrever um arquivo em disco e passar o caminho como uma string. Isso cria sobrecarga adicional nas operações de leitura e escrita do sistema de arquivos.
O reconhecimento dessas limitações levou a equipe da Meta a desenvolver uma nova arquitetura React Native, onde Bridge é substituído por JSI (JavaScript Interface) e Turbo Module. JSI permite chamar métodos nativos diretamente, sem serialização, eliminando a principal desvantagem do Bridge.
A comparação de Bridge e Turbo Module mostra diferenças fundamentais nas abordagens arquitetônicas. Bridge usa uma fila de mensagens assíncrona com serialização JSON, enquanto Turbo Module funciona através do JSI — uma interface direta entre JavaScript e C++ que permite chamar métodos nativos de forma síncrona sem conversão de dados.
| Característica | Bridge | Turbo Module |
|---|---|---|
| Tipo de chamada | Assíncrona | Síncrona e assíncrona |
| Serialização | JSON em cada chamada | Objetos JSI sem cópia |
| Desempenho | Médio | Alto |
| Tipagem | Dinâmica | Estática (Codegen) |
| Carregamento | Todos os módulos na inicialização | Preguiçoso (sob demanda) |
A escolha entre Bridge e Turbo Module depende da versão do React Native. Para projetos no React Native 0.72 e anteriores, Bridge continua sendo o mecanismo principal. A partir do React Native 0.73, Metro e a nova arquitetura são suportados em paralelo, permitindo migração gradual. A transição completa para Turbo Module requer atualização para React Native 0.76+ e habilitação da nova arquitetura na configuração.
Vamos percorrer o ciclo completo de criação e uso de um Native Module através do Bridge usando um módulo de calendário como exemplo. O módulo criará um evento e retornará seu identificador. Este exemplo cobre a configuração para ambas as plataformas — iOS e Android.
No Android, um Native Module é criado como uma classe Java que estende ReactContextBaseJavaModule. A anotação @ReactMethod exporta o método para o Bridge. Para Promise, a interface Promise de com.facebook.react.bridge é usada.
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());
}
}
}
O módulo é registrado através de @ReactModule ou manualmente no pacote do aplicativo. O React Native detecta automaticamente e o adiciona ao Bridge. Após o registro, o módulo está disponível a partir do JavaScript via 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();
}
}
É importante notar que o Bridge requer reinicialização do aplicativo ao adicionar novos módulos, já que o mapa de configuração é construído uma vez durante a inicialização. Isso o diferencia do Turbo Module, que carrega de forma preguiçosa e suporta recarga a quente de módulos sem reinicialização.
Perguntas frequentes
Bridge sempre usa uma fila assíncrona e serialização JSON, enquanto a transferência direta via JSI funciona de forma síncrona e sem cópia de dados. Bridge cria latência de serialização, mas garante isolamento de threads.
Não, Bridge suporta apenas chamadas assíncronas. A interação síncrona requer a nova arquitetura com JSI e Turbo Module. Esta é uma das principais limitações que foi resolvida no React Native 0.76+.
Bridge suporta tipos serializáveis para JSON: strings, números, valores booleanos, arrays, dicionários (objetos). Dados binários como imagens devem ser transferidos através do sistema de arquivos ou codificação base64.
Para medição, use React DevTools e o perfilador React Native. A aba Performance mostra o número de mensagens na fila do Bridge e as latências. O pacote react-native-bridge-spy também está disponível para monitoramento de tráfego.
É recomendado migrar em projetos exigentes em desempenho ou ao criar novos aplicativos no React Native 0.76+. Para projetos existentes, a migração pode ser gradual — ambas as arquiteturas funcionam em paralelo.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também