Moduły natywne dla Androida
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Ta strona została przetłumaczona przez PageTurner AI (beta). Nie jest oficjalnie zatwierdzona przez projekt. Znalazłeś błąd? Zgłoś problem →
Native Module i Native Components to nasze stabilne technologie używane w starszej architekturze. Zostaną one wycofane w przyszłości, gdy Nowa Architektura stanie się stabilna. Nowa Architektura wykorzystuje Turbo Native Module i Fabric Native Components, aby osiągnąć podobne rezultaty.
Witamy w module natywnych modułów dla Androida. Zacznij od przeczytania Wprowadzenia do modułów natywnych, aby dowiedzieć się, czym są moduły natywne.
Tworzenie natywnego modułu kalendarza
W tym przewodniku utworzysz natywny moduł CalendarModule, który umożliwi dostęp do interfejsów API kalendarza Androida z poziomu JavaScript. Pod koniec będziesz mógł wywołać z JavaScriptu metodę CalendarModule.createCalendarEvent('Dinner Party', 'My House');, co spowoduje wywołanie metody Java/Kotlin tworzącej wydarzenie w kalendarzu.
Konfiguracja
Aby rozpocząć, otwórz projekt Android w swojej aplikacji React Native w Android Studio. Projekt Android w aplikacji React Native znajduje się w następującej lokalizacji:

Zalecamy używanie Android Studio do pisania kodu natywnego. Android Studio to IDE stworzone do tworzenia aplikacji na Androida, a jego używanie pomaga szybko rozwiązywać drobne problemy, takie jak błędy składni kodu.
Zalecamy również włączenie demon Gradle, aby przyspieszyć budowanie podczas iteracji kodu Java/Kotlin.
Tworzenie własnego pliku modułu natywnego
Pierwszym krokiem jest utworzenie pliku Java/Kotlin (CalendarModule.java lub CalendarModule.kt) w folderze android/app/src/main/java/com/your-app-name/ (folder jest taki sam dla Kotlina i Javy). Ten plik Java/Kotlin będzie zawierał klasę Java/Kotlin twojego modułu natywnego.

Następnie dodaj następującą zawartość:
- Java
- Kotlin
package com.your-apps-package-name; // replace your-apps-package-name with your app’s package name
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;
public class CalendarModule extends ReactContextBaseJavaModule {
CalendarModule(ReactApplicationContext context) {
super(context);
}
}
package com.your-apps-package-name; // replace your-apps-package-name with your app’s package name
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
class CalendarModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {...}
Jak widać, twoja klasa CalendarModule rozszerza klasę ReactContextBaseJavaModule. W przypadku Androida natywne moduły Java/Kotlin są pisane jako klasy rozszerzające ReactContextBaseJavaModule i implementujące funkcjonalność wymaganą przez JavaScript.
Warto zauważyć, że technicznie klasy Java/Kotlin muszą jedynie rozszerzać klasę
BaseJavaModulelub implementować interfejsNativeModule, aby React Native uznał je za moduł natywny.
Zalecamy jednak używanie
ReactContextBaseJavaModule, jak pokazano powyżej.ReactContextBaseJavaModulezapewnia dostęp doReactApplicationContext(RAC), co jest przydatne dla modułów natywnych wymagających integracji z metodami cyklu życia aktywności. UżycieReactContextBaseJavaModuleułatwi także przyszłe zapewnienie bezpieczeństwa typów w twoim module. W kontekście bezpieczeństwa typów (planowanego w przyszłych wersjach) React Native analizuje specyfikację JavaScript każdego modułu i generuje abstrakcyjną klasę bazową rozszerzającąReactContextBaseJavaModule.
Nazwa modułu
Wszystkie natywne moduły Java/Kotlin w Androidzie muszą implementować metodę getName(). Metoda ta zwraca ciąg znaków reprezentujący nazwę modułu natywnego. Moduł natywny może być następnie dostępny w JavaScripcie za pomocą swojej nazwy. Na przykład, w poniższym fragmencie kodu, getName() zwraca "CalendarModule".
- Java
- Kotlin
// add to CalendarModule.java
@Override
public String getName() {
return "CalendarModule";
}
// add to CalendarModule.kt
override fun getName() = "CalendarModule"
Moduł natywny może być następnie dostępny w JS w następujący sposób:
const {CalendarModule} = ReactNative.NativeModules;
Eksportowanie natywnej metody do JavaScriptu
Następnie musisz dodać do modułu natywnego metodę, która będzie tworzyć wydarzenia w kalendarzu i może być wywoływana z JavaScriptu. Wszystkie metody modułów natywnych przeznaczone do wywołania z JavaScriptu muszą być opatrzone adnotacją @ReactMethod.
Skonfiguruj metodę createCalendarEvent() dla CalendarModule, którą można wywołać w JS za pomocą CalendarModule.createCalendarEvent(). Na razie metoda będzie przyjmować nazwę i lokalizację jako ciągi znaków. Opcje typów argumentów zostaną omówione wkrótce.
- Java
- Kotlin
@ReactMethod
public void createCalendarEvent(String name, String location) {
}
@ReactMethod fun createCalendarEvent(name: String, location: String) {}
Dodaj log debugowania w metodzie, aby potwierdzić, że została wywołana, gdy wywołasz ją z aplikacji. Poniżej znajduje się przykład, jak można zaimportować i użyć klasy Log z pakietu util Androida:
- Java
- Kotlin
import android.util.Log;
@ReactMethod
public void createCalendarEvent(String name, String location) {
Log.d("CalendarModule", "Create event called with name: " + name
+ " and location: " + location);
}
import android.util.Log
@ReactMethod
fun createCalendarEvent(name: String, location: String) {
Log.d("CalendarModule", "Create event called with name: $name and location: $location")
}
Po zakończeniu implementacji modułu natywnego i podłączeniu go w JavaScripcie, możesz wykonać te kroki, aby wyświetlić logi z aplikacji.
Metody synchroniczne
Możesz przekazać parametr isBlockingSynchronousMethod = true do metody natywnej, aby oznaczyć ją jako metodę synchroniczną.
- Java
- Kotlin
@ReactMethod(isBlockingSynchronousMethod = true)
@ReactMethod(isBlockingSynchronousMethod = true)
Obecnie nie zalecamy tego podejścia, ponieważ wywoływanie metod synchronicznie może powodować znaczne spadki wydajności i wprowadzać błędy związane z wątkami do modułów natywnych. Dodatkowo, jeśli włączysz isBlockingSynchronousMethod, Twoja aplikacja nie będzie mogła korzystać z debugera Google Chrome. Wynika to z faktu, że metody synchroniczne wymagają współdzielenia pamięci między maszyną wirtualną JavaScript a aplikacją. W przypadku debugera Chrome, React Native działa wewnątrz maszyny wirtualnej JS w przeglądarce i komunikuje się asynchronicznie z urządzeniami mobilnymi poprzez WebSockety.
Rejestracja modułu (specyficzne dla Androida)
Po napisaniu modułu natywnego należy go zarejestrować w React Native. W tym celu musisz dodać moduł do ReactPackage i zarejestrować ReactPackage w React Native. Podczas inicjalizacji, React Native iteruje po wszystkich pakietach i dla każdego ReactPackage rejestruje zawarte w nim moduły natywne.
React Native wywołuje metodę createNativeModules() w ReactPackage, aby uzyskać listę modułów natywnych do zarejestrowania. W przypadku Androida, jeśli moduł nie zostanie utworzony i zwrócony w createNativeModules, nie będzie dostępny z poziomu JavaScript.
Aby dodać moduł natywny do ReactPackage, najpierw utwórz nową klasę Java/Kotlin o nazwie (MyAppPackage.java lub MyAppPackage.kt) implementującą ReactPackage w folderze android/app/src/main/java/com/your-app-name/:
Następnie dodaj następującą zawartość:
- Java
- Kotlin
package com.your-app-name; // replace your-app-name with your app’s name
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class MyAppPackage implements ReactPackage {
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
@Override
public List<NativeModule> createNativeModules(
ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new CalendarModule(reactContext));
return modules;
}
}
package com.your-app-name // replace your-app-name with your app’s name
import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager
class MyAppPackage : ReactPackage {
override fun createViewManagers(
reactContext: ReactApplicationContext
): MutableList<ViewManager<View, ReactShadowNode<*>>> = mutableListOf()
override fun createNativeModules(
reactContext: ReactApplicationContext
): MutableList<NativeModule> = listOf(CalendarModule(reactContext)).toMutableList()
}
Ten plik importuje utworzony moduł natywny CalendarModule. Następnie tworzy instancję CalendarModule w funkcji createNativeModules() i zwraca ją jako listę NativeModules do zarejestrowania. Jeśli dodasz później więcej modułów natywnych, możesz utworzyć ich instancje i dodać do zwracanej listy.
Ważna uwaga: ta metoda rejestracji modułów inicjalizuje je wszystkie podczas uruchamiania aplikacji, co wydłuża czas startu. Alternatywą jest użycie TurboReactPackage. Zamiast
createNativeModuleszwracającej listę instancji modułów, TurboReactPackage implementuje metodęgetModule(String name, ReactApplicationContext rac)tworzącą obiekt modułu dopiero gdy jest potrzebny. Obecnie implementacja TurboReactPackage jest bardziej złożona: opróczgetModule()wymaga implementacjigetReactModuleInfoProvider()zwracającego listę wszystkich modułów wraz z funkcją ich tworzenia (przykład tutaj). Chociaż TurboReactPackage przyspiesza start aplikacji, obecnie jego implementacja jest bardziej skomplikowana - zalecamy ostrożność przy jego stosowaniu.
Aby zarejestrować pakiet CalendarModule, dodaj MyAppPackage do listy pakietów zwracanych przez metodę getPackages() w ReactNativeHost. Otwórz plik MainApplication.java lub MainApplication.kt znajdujący się w ścieżce: android/app/src/main/java/com/your-app-name/.
Znajdź metodę getPackages() w ReactNativeHost i dodaj swój pakiet do listy pakietów zwracanej przez getPackages():
- Java
- Kotlin
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
packages.add(new MyAppPackage());
return packages;
}
override fun getPackages(): List<ReactPackage> =
PackageList(this).packages.apply {
// Packages that cannot be autolinked yet can be added manually here, for example:
// add(MyReactNativePackage())
add(MyAppPackage())
}
Pomyślnie zarejestrowałeś moduł natywny dla Androida!
Przetestuj swoją implementację
W tym momencie masz już podstawową strukturę modułu natywnego dla Androida. Przetestuj ją, uzyskując dostęp do modułu natywnego i wywołując jego eksportowaną metodę w JavaScript.
Znajdź miejsce w swojej aplikacji, gdzie chcesz dodać wywołanie metody createCalendarEvent() modułu natywnego. Poniżej znajduje się przykład komponentu NewModuleButton, który możesz dodać w swojej aplikacji. Moduł natywny możesz wywołać wewnątrz funkcji onPress() komponentu NewModuleButton.
import React from 'react';
import {NativeModules, Button} from 'react-native';
const NewModuleButton = () => {
const onPress = () => {
console.log('We will invoke the native module here!');
};
return (
<Button
title="Click to invoke your native module!"
color="#841584"
onPress={onPress}
/>
);
};
export default NewModuleButton;
Aby uzyskać dostęp do modułu natywnego z JavaScript, najpierw zaimportuj NativeModules z React Native:
import {NativeModules} from 'react-native';
Następnie możesz uzyskać dostęp do modułu natywnego CalendarModule poprzez NativeModules.
const {CalendarModule} = NativeModules;
Gdy moduł CalendarModule jest już dostępny, możesz wywołać metodę natywną createCalendarEvent(). Poniżej dodano ją do metody onPress() w NewModuleButton:
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};
Ostatnim krokiem jest przebudowanie aplikacji React Native, aby mieć dostęp do najnowszego kodu natywnego (z twoim nowym modułem natywnym!). W wierszu poleceń, w lokalizacji aplikacji React Native, uruchom:
- npm
- Yarn
npm run android
yarn android
Budowanie w trakcie iteracji
Podczas pracy z tymi przewodnikami i iterowania nad modułem natywnym, będziesz musiał przebudować natywną część aplikacji, aby mieć dostęp do najnowszych zmian z poziomu JavaScript. Dzieje się tak dlatego, że kod piszesz w natywnej części aplikacji. Podczas gdy metro bundler React Native może śledzić zmiany w JavaScript i przebudowywać aplikację w locie, nie robi tego dla kodu natywnego. Jeśli więc chcesz przetestować swoje najnowsze zmiany w kodzie natywnym, musisz przebudować aplikację przy użyciu powyższego polecenia.
Podsumowanie ✨
Powinieneś już móc wywoływać metodę createCalendarEvent() swojego modułu natywnego w aplikacji. W naszym przykładzie dzieje się to przez naciśnięcie przycisku NewModuleButton. Możesz to potwierdzić, przeglądając logi skonfigurowane w metodzie createCalendarEvent(). Możesz postępować zgodnie z tymi instrukcjami, aby wyświetlić logi ADB w swojej aplikacji. Powinieneś wtedy móc wyszukać swoją wiadomość Log.d (w naszym przykładzie "Create event called with name: testName and location: testLocation") i zobaczyć logowaną wiadomość przy każdym wywołaniu metody modułu natywnego.

W tym momencie utworzyłeś natywny moduł Androida i wywołałeś jego natywną metodę z JavaScript w aplikacji React Native. Możesz kontynuować czytanie, aby dowiedzieć się więcej o dostępnych typach argumentów dla metod modułów natywnych oraz o konfiguracji callbacków i promise'ów.
Poza modułem natywnym kalendarza
Lepszy eksport modułu natywnego
Importowanie modułu natywnego poprzez pobieranie go z NativeModules, jak pokazano powyżej, jest nieco toporne.
Aby użytkownicy twojego modułu natywnego nie musieli tego robić za każdym razem, możesz utworzyć wrapper JavaScript dla modułu. Utwórz nowy plik JavaScript o nazwie CalendarModule.js z następującą zawartością:
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
export default CalendarModule;
Ten plik JavaScript jest również dobrym miejscem do dodawania funkcjonalności po stronie JavaScript. Na przykład, jeśli używasz systemu typów takiego jak TypeScript, możesz tutaj dodać adnotacje typów dla swojego modułu natywnego. Mimo że React Native nie obsługuje jeszcze bezpieczeństwa typów od strony natywnej do JS, cały twój kod JS będzie bezpieczny typowo. Zrobienie tego ułatwi również późniejsze przejście na typowo bezpieczne moduły natywne. Poniżej znajduje się przykład dodania bezpieczeństwa typów dla CalendarModule:
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
*
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
interface CalendarInterface {
createCalendarEvent(name: string, location: string): void;
}
export default CalendarModule as CalendarInterface;
W innych plikach JavaScript możesz uzyskać dostęp do modułu natywnego i wywołać jego metodę w następujący sposób:
import CalendarModule from './CalendarModule';
CalendarModule.createCalendarEvent('foo', 'bar');
Zakłada się, że miejsce importu
CalendarModuleznajduje się w tej samej hierarchii coCalendarModule.js. W razie potrzeby dostosuj ścieżkę względną importu.
Typy argumentów
Typy argumentów
| Java | Kotlin | JavaScript |
|---|---|---|
| Boolean | Boolean | ?boolean |
| boolean | boolean | |
| Double | Double | ?number |
| double | number | |
| String | String | string |
| Callback | Callback | Function |
| Promise | Promise | Promise |
| ReadableMap | ReadableMap | Object |
| ReadableArray | ReadableArray | Array |
Następujące typy są obecnie obsługiwane, ale nie będą wspierane w TurboModules. Unikaj ich używania:
- Integer Java/Kotlin -> ?number
- Float Java/Kotlin -> ?number
- int Java -> number
- float Java -> number
Dla typów argumentów niewymienionych powyżej, konieczne będzie samodzielne obsłużenie konwersji. Na przykład w Androidzie konwersja Date nie jest domyślnie wspierana. Możesz samodzielnie obsłużyć konwersję na typ Date w metodzie natywnej w następujący sposób:
- Java
- Kotlin
String dateFormat = "yyyy-MM-dd";
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
Calendar eStartDate = Calendar.getInstance();
try {
eStartDate.setTime(sdf.parse(startDate));
}
val dateFormat = "yyyy-MM-dd"
val sdf = SimpleDateFormat(dateFormat, Locale.US)
val eStartDate = Calendar.getInstance()
try {
sdf.parse(startDate)?.let {
eStartDate.time = it
}
}
Eksportowanie stałych
Moduł natywny może eksportować stałe poprzez implementację metody getConstants(), dostępnej w JS. Poniżej zaimplementujesz getConstants() i zwrócisz Mapę zawierającą stałą DEFAULT_EVENT_NAME, dostępną w JavaScripcie:
- Java
- Kotlin
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("DEFAULT_EVENT_NAME", "New Event");
return constants;
}
override fun getConstants(): MutableMap<String, Any> =
hashMapOf("DEFAULT_EVENT_NAME" to "New Event")
Do stałej można następnie uzyskać dostęp poprzez wywołanie getConstants na module natywnym w JS:
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);
Teoretycznie możliwy jest bezpośredni dostęp do stałych eksportowanych w getConstants() bezpośrednio z obiektu modułu natywnego. To podejście nie będzie wspierane w TurboModules, dlatego zachęcamy społeczność do przejścia na powyższą metodę, aby uniknąć migracji w przyszłości.
Obecnie stałe są eksportowane tylko podczas inicjalizacji - zmiana wartości getConstants w trakcie działania nie wpłynie na środowisko JavaScript. To ulegnie zmianie w Turbomodules, gdzie
getConstants()stanie się zwykłą metodą modułu natywnego wywoływaną za każdym razem po stronie natywnej.
Callbacki
Moduły natywne obsługują również specjalny rodzaj argumentu: callback. Callbacki służą do przekazywania danych z Java/Kotlin do JavaScript dla metod asynchronicznych. Mogą też asynchronicznie wykonywać JavaScript ze strony natywnej.
Aby utworzyć metodę modułu natywnego z callbackiem, najpierw zaimportuj interfejs Callback, a następnie dodaj nowy parametr typu Callback do swojej metody. Istnieją pewne niuanse związane z callbackami, które wkrótce zostaną usunięte w TurboModules. Po pierwsze, możesz mieć tylko dwa callbacki w argumentach funkcji - successCallback i failureCallback. Dodatkowo, ostatni argument wywołania metody modułu natywnego, jeśli jest funkcją, traktowany jest jako successCallback, a przedostatni jako failureCallback.
- Java
- Kotlin
import com.facebook.react.bridge.Callback;
@ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
}
import com.facebook.react.bridge.Callback
@ReactMethod fun createCalendarEvent(name: String, location: String, callback: Callback) {}
Callback możesz wywołać w metodzie Java/Kotlin, przekazując dowolne dane do JavaScript. Pamiętaj, że możesz przekazywać tylko dane serializowalne z kodu natywnego do JavaScript. Jeśli potrzebujesz przekazać obiekt natywny, użyj WriteableMaps, dla kolekcji użyj WritableArrays. Ważne jest również, że callback nie jest wywoływany natychmiast po zakończeniu funkcji natywnej. Poniżej ID wydarzenia utworzonego wcześniej jest przekazywane do callbacka.
- Java
- Kotlin
@ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
Integer eventId = ...
callBack.invoke(eventId);
}
@ReactMethod
fun createCalendarEvent(name: String, location: String, callback: Callback) {
val eventId = ...
callback.invoke(eventId)
}
Tę metodę można następnie wywołać w JavaScripcie:
const onPress = () => {
CalendarModule.createCalendarEvent(
'Party',
'My House',
eventId => {
console.log(`Created a new event with id ${eventId}`);
},
);
};
Inny ważny szczegół: metoda modułu natywnego może wywołać tylko jeden callback, jeden raz. Oznacza to, że możesz wywołać albo callback sukcesu, albo błędu, ale nie oba, i każdy callback może zostać wywołany maksymalnie raz. Moduł natywny może jednak przechować callback i wywołać go później.
Istnieją dwa podejścia do obsługi błędów z callbackami. Pierwsze to naśladowanie konwencji Node.js i traktowanie pierwszego argumentu przekazanego do callbacka jako obiektu błędu.
- Java
- Kotlin
@ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
Integer eventId = ...
callBack.invoke(null, eventId);
}
@ReactMethod
fun createCalendarEvent(name: String, location: String, callback: Callback) {
val eventId = ...
callback.invoke(null, eventId)
}
W JavaScripcie możesz sprawdzić pierwszy argument, aby wykryć błąd:
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`Error found! ${error}`);
}
console.log(`event id ${eventId} returned`);
},
);
};
Inna opcja to użycie osobnych callbacków onSuccess i onFailure:
- Java
- Kotlin
@ReactMethod
public void createCalendarEvent(String name, String location, Callback myFailureCallback, Callback mySuccessCallback) {
}
@ReactMethod
fun createCalendarEvent(
name: String,
location: String,
myFailureCallback: Callback,
mySuccessCallback: Callback
) {}
W JavaScripcie możesz dodać osobne callbacki dla odpowiedzi sukcesu i błędu:
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
error => {
console.error(`Error found! ${error}`);
},
eventId => {
console.log(`event id ${eventId} returned`);
},
);
};
Promisy
Moduły natywne mogą również zwracać Promise, co upraszcza kod JavaScript, szczególnie przy użyciu składni async/await z ES2016. Gdy ostatni parametr metody Java/Kotlin jest typu Promise, odpowiadająca jej metoda JS zwróci obiekt Promise.
Refaktoryzacja powyższego kodu do użycia promisa zamiast callbacków wygląda następująco:
- Java
- Kotlin
import com.facebook.react.bridge.Promise;
@ReactMethod
public void createCalendarEvent(String name, String location, Promise promise) {
try {
Integer eventId = ...
promise.resolve(eventId);
} catch(Exception e) {
promise.reject("Create Event Error", e);
}
}
import com.facebook.react.bridge.Promise
@ReactMethod
fun createCalendarEvent(name: String, location: String, promise: Promise) {
try {
val eventId = ...
promise.resolve(eventId)
} catch (e: Throwable) {
promise.reject("Create Event Error", e)
}
}
Podobnie jak w przypadku callbacków, metoda modułu natywnego może albo odrzucić (reject), albo rozwiązać (resolve) promesę (ale nie obie naraz) i może to zrobić najwyżej raz. Oznacza to, że możesz wywołać albo callback sukcesu, albo błedu (nie oba) i każdy callback można wywołać tylko raz. Moduł może jednak przechować callback i wywołać go później.
Odpowiednik tej metody w JavaScript zwraca Promisa. Oznacza to, że możesz użyć słowa kluczowego await wewnątrz funkcji asynchronicznej, aby ją wywołać i poczekać na wynik:
const onSubmit = async () => {
try {
const eventId = await CalendarModule.createCalendarEvent(
'Party',
'My House',
);
console.log(`Created a new event with id ${eventId}`);
} catch (e) {
console.error(e);
}
};
Metoda reject przyjmuje różne kombinacje następujących argumentów:
- Java
- Kotlin
String code, String message, WritableMap userInfo, Throwable throwable
code: String, message: String, userInfo: WritableMap, throwable: Throwable
Szczegóły znajdziesz w interfejsie Promise.java. Jeśli userInfo nie zostanie podany, ReactNative ustawi go na null. Dla pozostałych parametrów React Native użyje wartości domyślnych. Argument message dostarcza message błędu wyświetlany na szczycie stosu wywołań. Poniżej przykład komunikatu błędu w JavaScript wynikającego z wywołania reject w Java/Kotlin.
Wywołanie reject w Javie/Kotlinie:
- Java
- Kotlin
promise.reject("Create Event error", "Error parsing date", e);
promise.reject("Create Event error", "Error parsing date", e)
Komunikat błędu w aplikacji React Native gdy promis jest odrzucony:

Wysyłanie zdarzeń do JavaScript
Moduły natywne mogą sygnalizować zdarzenia do JavaScript bez bezpośredniego wywoływania. Na przykład możesz chcieć powiadomić JavaScript o przypomnieniu, że wkrótce nastąpi zdarzenie z kalendarza w natywnej aplikacji kalendarza Androida. Najprostszym sposobem jest użycie RCTDeviceEventEmitter, który można uzyskać z ReactContext jak w poniższym fragmencie kodu.
- Java
- Kotlin
...
import com.facebook.react.modules.core.DeviceEventManagerModule;
import com.facebook.react.bridge.WritableMap;
import com.facebook.react.bridge.Arguments;
...
private void sendEvent(ReactContext reactContext,
String eventName,
@Nullable WritableMap params) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
.emit(eventName, params);
}
private int listenerCount = 0;
@ReactMethod
public void addListener(String eventName) {
if (listenerCount == 0) {
// Set up any upstream listeners or background tasks as necessary
}
listenerCount += 1;
}
@ReactMethod
public void removeListeners(Integer count) {
listenerCount -= count;
if (listenerCount == 0) {
// Remove upstream listeners, stop unnecessary background tasks
}
}
...
WritableMap params = Arguments.createMap();
params.putString("eventProperty", "someValue");
...
sendEvent(reactContext, "EventReminder", params);
...
import com.facebook.react.bridge.WritableMap
import com.facebook.react.bridge.Arguments
import com.facebook.react.modules.core.DeviceEventManagerModule
...
private fun sendEvent(reactContext: ReactContext, eventName: String, params: WritableMap?) {
reactContext
.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
.emit(eventName, params)
}
private var listenerCount = 0
@ReactMethod
fun addListener(eventName: String) {
if (listenerCount == 0) {
// Set up any upstream listeners or background tasks as necessary
}
listenerCount += 1
}
@ReactMethod
fun removeListeners(count: Int) {
listenerCount -= count
if (listenerCount == 0) {
// Remove upstream listeners, stop unnecessary background tasks
}
}
...
val params = Arguments.createMap().apply {
putString("eventProperty", "someValue")
}
...
sendEvent(reactContext, "EventReminder", params)
Moduły JavaScript mogą następnie rejestrować się do odbierania zdarzeń przez addListener w klasie NativeEventEmitter.
import {NativeEventEmitter, NativeModules} from 'react-native';
...
useEffect(() => {
const eventEmitter = new NativeEventEmitter(NativeModules.ToastExample);
let eventListener = eventEmitter.addListener('EventReminder', event => {
console.log(event.eventProperty) // "someValue"
});
// Removes the listener once unmounted
return () => {
eventListener.remove();
};
}, []);
Uzyskiwanie wyniku aktywności z startActivityForResult
Będziesz musiał nasłuchiwać onActivityResult, jeśli chcesz uzyskać wyniki z aktywności rozpoczętej przez startActivityForResult. Aby to zrobić, musisz rozszerzyć BaseActivityEventListener lub zaimplementować ActivityEventListener. Pierwsze rozwiązanie jest preferowane, ponieważ jest bardziej odporne na zmiany w API. Następnie musisz zarejestrować nasłuchiwacz w konstruktorze modułu w następujący sposób:
- Java
- Kotlin
reactContext.addActivityEventListener(mActivityResultListener);
reactContext.addActivityEventListener(mActivityResultListener);
Teraz możesz nasłuchiwać onActivityResult implementując następującą metodę:
- Java
- Kotlin
@Override
public void onActivityResult(
final Activity activity,
final int requestCode,
final int resultCode,
final Intent intent) {
// Your logic here
}
override fun onActivityResult(
activity: Activity?,
requestCode: Int,
resultCode: Int,
intent: Intent?
) {
// Your logic here
}
Zaimplementujmy podstawowy selektor obrazów, aby to zademonstrować. Selektor obrazów będzie udostępniał metodę pickImage w JavaScript, która po wywołaniu zwróci ścieżkę obrazu.
- Java
- Kotlin
public class ImagePickerModule extends ReactContextBaseJavaModule {
private static final int IMAGE_PICKER_REQUEST = 1;
private static final String E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST";
private static final String E_PICKER_CANCELLED = "E_PICKER_CANCELLED";
private static final String E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER";
private static final String E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND";
private Promise mPickerPromise;
private final ActivityEventListener mActivityEventListener = new BaseActivityEventListener() {
@Override
public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent intent) {
if (requestCode == IMAGE_PICKER_REQUEST) {
if (mPickerPromise != null) {
if (resultCode == Activity.RESULT_CANCELED) {
mPickerPromise.reject(E_PICKER_CANCELLED, "Image picker was cancelled");
} else if (resultCode == Activity.RESULT_OK) {
Uri uri = intent.getData();
if (uri == null) {
mPickerPromise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found");
} else {
mPickerPromise.resolve(uri.toString());
}
}
mPickerPromise = null;
}
}
}
};
ImagePickerModule(ReactApplicationContext reactContext) {
super(reactContext);
// Add the listener for `onActivityResult`
reactContext.addActivityEventListener(mActivityEventListener);
}
@Override
public String getName() {
return "ImagePickerModule";
}
@ReactMethod
public void pickImage(final Promise promise) {
Activity currentActivity = getCurrentActivity();
if (currentActivity == null) {
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist");
return;
}
// Store the promise to resolve/reject when picker returns data
mPickerPromise = promise;
try {
final Intent galleryIntent = new Intent(Intent.ACTION_PICK);
galleryIntent.setType("image/*");
final Intent chooserIntent = Intent.createChooser(galleryIntent, "Pick an image");
currentActivity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST);
} catch (Exception e) {
mPickerPromise.reject(E_FAILED_TO_SHOW_PICKER, e);
mPickerPromise = null;
}
}
}
class ImagePickerModule(reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
private var pickerPromise: Promise? = null
private val activityEventListener =
object : BaseActivityEventListener() {
override fun onActivityResult(
activity: Activity?,
requestCode: Int,
resultCode: Int,
intent: Intent?
) {
if (requestCode == IMAGE_PICKER_REQUEST) {
pickerPromise?.let { promise ->
when (resultCode) {
Activity.RESULT_CANCELED ->
promise.reject(E_PICKER_CANCELLED, "Image picker was cancelled")
Activity.RESULT_OK -> {
val uri = intent?.data
uri?.let { promise.resolve(uri.toString())}
?: promise.reject(E_NO_IMAGE_DATA_FOUND, "No image data found")
}
}
pickerPromise = null
}
}
}
}
init {
reactContext.addActivityEventListener(activityEventListener)
}
override fun getName() = "ImagePickerModule"
@ReactMethod
fun pickImage(promise: Promise) {
val activity = currentActivity
if (activity == null) {
promise.reject(E_ACTIVITY_DOES_NOT_EXIST, "Activity doesn't exist")
return
}
pickerPromise = promise
try {
val galleryIntent = Intent(Intent.ACTION_PICK).apply { type = "image\/*" }
val chooserIntent = Intent.createChooser(galleryIntent, "Pick an image")
activity.startActivityForResult(chooserIntent, IMAGE_PICKER_REQUEST)
} catch (t: Throwable) {
pickerPromise?.reject(E_FAILED_TO_SHOW_PICKER, t)
pickerPromise = null
}
}
companion object {
const val IMAGE_PICKER_REQUEST = 1
const val E_ACTIVITY_DOES_NOT_EXIST = "E_ACTIVITY_DOES_NOT_EXIST"
const val E_PICKER_CANCELLED = "E_PICKER_CANCELLED"
const val E_FAILED_TO_SHOW_PICKER = "E_FAILED_TO_SHOW_PICKER"
const val E_NO_IMAGE_DATA_FOUND = "E_NO_IMAGE_DATA_FOUND"
}
}
Nasłuchiwanie zdarzeń cyklu życia
Nasłuchiwanie zdarzeń cyklu życia aktywności, takich jak onResume, onPause itp., jest bardzo podobne do implementacji ActivityEventListener. Moduł musi implementować LifecycleEventListener. Następnie musisz zarejestrować nasłuchiwacz w konstruktorze modułu w następujący sposób:
- Java
- Kotlin
reactContext.addLifecycleEventListener(this);
reactContext.addLifecycleEventListener(this)
Teraz możesz nasłuchiwać zdarzeń cyklu życia aktywności, implementując następujące metody:
- Java
- Kotlin
@Override
public void onHostResume() {
// Activity `onResume`
}
@Override
public void onHostPause() {
// Activity `onPause`
}
@Override
public void onHostDestroy() {
// Activity `onDestroy`
}
override fun onHostResume() {
// Activity `onResume`
}
override fun onHostPause() {
// Activity `onPause`
}
override fun onHostDestroy() {
// Activity `onDestroy`
}
Zarządzanie wątkami
Do tej pory na Androidzie wszystkie asynchroniczne metody modułów natywnych wykonują się w jednym wątku. Moduły natywne nie powinny zakładać, w którym wątku są wywoływane, ponieważ obecne przypisanie może ulec zmianie w przyszłości. Jeśli wymagane jest blokujące wywołanie, ciężka praca powinna być przekazana do wewnętrznie zarządzanego wątku roboczego, a callbacki dystrybuowane stamtąd.