Saltar al contenido principal

Módulos nativos de Android

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

información

Native Module y Native Components son nuestras tecnologías estables utilizadas por la arquitectura heredada. Serán desaprobadas en el futuro cuando la Nueva Arquitectura sea estable. La Nueva Arquitectura utiliza Turbo Native Module y Fabric Native Components para lograr resultados similares.

Bienvenido a los módulos nativos para Android. Te recomendamos comenzar leyendo la Introducción a los módulos nativos para entender qué son los módulos nativos.

Crear un módulo nativo de calendario

En esta guía crearás un módulo nativo CalendarModule que te permitirá acceder a las APIs de calendario de Android desde JavaScript. Al final podrás llamar CalendarModule.createCalendarEvent('Dinner Party', 'My House'); desde JavaScript, lo que invocará un método Java/Kotlin que crea eventos de calendario.

Configuración inicial

Para comenzar, abre el proyecto Android dentro de tu aplicación React Native en Android Studio. Puedes encontrar tu proyecto Android aquí dentro de una app React Native:

Image of opening up an Android project within a React Native app inside of Android Studio.
Image of where you can find your Android project

Te recomendamos usar Android Studio para escribir tu código nativo. Android Studio es un IDE diseñado para desarrollo en Android y usarlo te ayudará a resolver problemas menores como errores de sintaxis rápidamente.

También recomendamos habilitar el Demonio de Gradle para acelerar las compilaciones mientras trabajas con código Java/Kotlin.

Crear un archivo de módulo nativo personalizado

El primer paso es crear el archivo Java/Kotlin (CalendarModule.java o CalendarModule.kt) dentro de la carpeta android/app/src/main/java/com/your-app-name/ (la carpeta es la misma para Kotlin y Java). Este archivo Java/Kotlin contendrá tu clase de módulo nativo.

Image of adding a class called CalendarModule.java within the Android Studio.
Image of how to add the CalendarModuleClass

Luego agrega el siguiente contenido:

java
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);
}
}

Como ves, tu clase CalendarModule extiende ReactContextBaseJavaModule. En Android, los módulos nativos Java/Kotlin se implementan como clases que extienden ReactContextBaseJavaModule e implementan la funcionalidad requerida por JavaScript.

nota

Vale la pena destacar que técnicamente las clases Java/Kotlin solo necesitan extender la clase BaseJavaModule o implementar la interfaz NativeModule para ser consideradas un Módulo Nativo por React Native.

Sin embargo, recomendamos usar ReactContextBaseJavaModule como se mostró anteriormente. ReactContextBaseJavaModule brinda acceso al ReactApplicationContext (RAC), útil para módulos nativos que necesitan integrarse con métodos del ciclo de vida de actividades. Usar ReactContextBaseJavaModule también facilitará que tu módulo nativo sea type-safe en el futuro. Para la seguridad de tipos en módulos nativos (próximamente en futuras versiones), React Native analizará el spec JavaScript de cada módulo nativo y generará una clase base abstracta que extienda ReactContextBaseJavaModule.

Nombre del módulo

Todos los módulos nativos Java/Kotlin en Android deben implementar el método getName(), que devuelve un string con el nombre del módulo nativo. Este nombre permite acceder al módulo desde JavaScript. Por ejemplo, en este fragmento getName() devuelve "CalendarModule".

java
// add to CalendarModule.java
@Override
public String getName() {
return "CalendarModule";
}

El módulo nativo podrá entonces accederse en JS así:

tsx
const {CalendarModule} = ReactNative.NativeModules;

Exportar un método nativo a JavaScript

Ahora deberás agregar un método a tu módulo nativo que cree eventos de calendario y pueda invocarse desde JavaScript. Todos los métodos de módulo nativo invocables desde JavaScript deben anotarse con @ReactMethod.

Configura un método createCalendarEvent() en CalendarModule que pueda invocarse en JS mediante CalendarModule.createCalendarEvent(). Por ahora, el método aceptará nombre y ubicación como strings. Las opciones de tipos de argumentos se cubrirán pronto.

java
@ReactMethod
public void createCalendarEvent(String name, String location) {
}

Agrega un log de depuración en el método para confirmar su invocación desde tu aplicación. Aquí un ejemplo de cómo importar y usar la clase Log del paquete android.util:

java
import android.util.Log;

@ReactMethod
public void createCalendarEvent(String name, String location) {
Log.d("CalendarModule", "Create event called with name: " + name
+ " and location: " + location);
}

Al terminar de implementar el módulo nativo y conectarlo en JavaScript, puedes seguir estos pasos para ver los logs de tu app.

Métodos Síncronos

Puedes pasar isBlockingSynchronousMethod = true a un método nativo para marcarlo como método síncrono.

java
@ReactMethod(isBlockingSynchronousMethod = true)

Actualmente no lo recomendamos, ya que llamar métodos de forma síncrona puede tener fuertes penalizaciones de rendimiento e introducir errores relacionados con hilos en tus módulos nativos. Además, ten en cuenta que si habilitas isBlockingSynchronousMethod, tu aplicación ya no podrá usar el depurador de Google Chrome. Esto se debe a que los métodos síncronos requieren que la máquina virtual de JavaScript (JS VM) comparta memoria con la aplicación. Para el depurador de Google Chrome, React Native se ejecuta dentro de la JS VM en Chrome y se comunica de forma asíncrona con los dispositivos móviles a través de WebSockets.

Registrar el módulo (específico de Android)

Una vez escrito un módulo nativo, debe registrarse con React Native. Para hacerlo, debes agregar tu módulo nativo a un ReactPackage y registrar ese ReactPackage con React Native. Durante la inicialización, React Native recorrerá todos los paquetes y, para cada ReactPackage, registrará cada módulo nativo dentro de él.

React Native invoca el método createNativeModules() en un ReactPackage para obtener la lista de módulos nativos a registrar. En Android, si un módulo no se instancia y devuelve en createNativeModules, no estará disponible desde JavaScript.

Para agregar tu módulo nativo a ReactPackage, primero crea una nueva clase Java/Kotlin llamada (MyAppPackage.java o MyAppPackage.kt) que implemente ReactPackage dentro de la carpeta android/app/src/main/java/com/your-app-name/:

Luego agrega el siguiente contenido:

java
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;
}

}

Este archivo importa el módulo nativo que creaste, CalendarModule. Luego instancia CalendarModule dentro de la función createNativeModules() y lo devuelve como una lista de NativeModules para registrar. Si agregas más módulos nativos posteriormente, también puedes instanciarlos y agregarlos a la lista que se devuelve aquí.

nota

Vale la pena destacar que esta forma de registrar módulos nativos inicializa de manera ansiosa todos los módulos nativos al inicio de la aplicación, lo que aumenta el tiempo de arranque. Puedes usar TurboReactPackage como alternativa. En lugar de createNativeModules (que devuelve una lista de objetos de módulos nativos instanciados), TurboReactPackage implementa un método getModule(String name, ReactApplicationContext rac) que crea el objeto del módulo nativo cuando es necesario. Actualmente TurboReactPackage es un poco más complejo de implementar. Además de implementar getModule(), debes implementar getReactModuleInfoProvider(), que devuelve una lista de todos los módulos nativos que el paquete puede instanciar junto con una función que los crea, ejemplo aquí. Nuevamente, usar TurboReactPackage permitirá que tu aplicación tenga un tiempo de arranque más rápido, pero actualmente es un poco engorroso de escribir. Por lo tanto, procede con precaución si decides usar TurboReactPackages.

Para registrar el paquete CalendarModule, debes agregar MyAppPackage a la lista de paquetes devueltos en el método getPackages() de ReactNativeHost. Abre tu archivo MainApplication.java o MainApplication.kt, que puedes encontrar en la ruta: android/app/src/main/java/com/your-app-name/.

Localiza el método getPackages() de ReactNativeHost y agrega tu paquete a la lista de paquetes que devuelve getPackages():

java
@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;
}

¡Ahora has registrado exitosamente tu módulo nativo para Android!

Prueba lo que has construido

En este punto, has configurado la estructura básica de tu módulo nativo en Android. Pruébalo accediendo al módulo nativo e invocando su método exportado en JavaScript.

Encuentra un lugar en tu aplicación donde quieras añadir una llamada al método createCalendarEvent() del módulo nativo. A continuación se muestra un ejemplo de un componente, NewModuleButton, que puedes añadir en tu app. Puedes invocar el módulo nativo dentro de la función onPress() de NewModuleButton.

tsx
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;

Para acceder a tu módulo nativo desde JavaScript, primero debes importar NativeModules desde React Native:

tsx
import {NativeModules} from 'react-native';

Luego puedes acceder al módulo nativo CalendarModule desde NativeModules.

tsx
const {CalendarModule} = NativeModules;

Ahora que tienes disponible el módulo nativo CalendarModule, puedes invocar tu método nativo createCalendarEvent(). A continuación se muestra cómo agregarlo al método onPress() en NewModuleButton:

tsx
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};

El paso final es reconstruir la aplicación React Native para tener disponible el último código nativo (¡con tu nuevo módulo nativo!). En tu línea de comandos, donde se encuentra la aplicación React Native, ejecuta:

shell
npm run android

Construyendo durante la iteración

Mientras trabajas en estas guías e iteras sobre tu módulo nativo, deberás realizar una reconstrucción nativa de tu aplicación para acceder a tus cambios más recientes desde JavaScript. Esto se debe a que el código que estás escribiendo reside en la parte nativa de tu aplicación. Si bien el bundler Metro de React Native puede observar cambios en JavaScript y reconstruir sobre la marcha, no lo hará para código nativo. Por lo tanto, si quieres probar tus últimos cambios nativos, debes reconstruir usando el comando anterior.

Recapitulación ✨

Ahora deberías poder invocar el método createCalendarEvent() de tu módulo nativo en la aplicación. En nuestro ejemplo esto ocurre al presionar NewModuleButton. Puedes confirmarlo viendo el registro que configuraste en tu método createCalendarEvent(). Puedes seguir estos pasos para ver los registros ADB en tu aplicación. Deberías poder buscar tu mensaje Log.d (en nuestro ejemplo "Create event called with name: testName and location: testLocation") y ver tu mensaje registrado cada vez que invocas el método de tu módulo nativo.

Image of logs.
Image of ADB logs in Android Studio

En este punto has creado un módulo nativo de Android e invocado su método nativo desde JavaScript en tu aplicación React Native. Puedes continuar leyendo para aprender más sobre temas como los tipos de argumentos disponibles para un método de módulo nativo y cómo configurar callbacks y promises.

Más allá de un módulo nativo de calendario

Mejor exportación de módulos nativos

Importar tu módulo nativo extrayéndolo de NativeModules como arriba es un poco engorroso.

Para evitar que los consumidores de tu módulo nativo necesiten hacer esto cada vez que quieran acceder a tu módulo, puedes crear un envoltorio JavaScript. Crea un nuevo archivo JavaScript llamado CalendarModule.js con este contenido:

tsx
/**
* 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;

Este archivo JavaScript también se convierte en un buen lugar para agregar funcionalidad del lado de JavaScript. Por ejemplo, si usas un sistema de tipos como TypeScript, puedes agregar anotaciones de tipo para tu módulo nativo aquí. Si bien React Native aún no admite seguridad de tipos de Nativo a JS, todo tu código JS será seguro. Hacer esto también facilitará la transición a módulos nativos con seguridad de tipos en el futuro. A continuación hay un ejemplo de cómo agregar seguridad de tipos a CalendarModule:

tsx
/**
* 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;

En tus otros archivos JavaScript puedes acceder al módulo nativo e invocar su método así:

tsx
import CalendarModule from './CalendarModule';
CalendarModule.createCalendarEvent('foo', 'bar');
nota

Esto asume que estás importando CalendarModule desde una jerarquía idéntica a CalendarModule.js. Actualiza la importación relativa según sea necesario.

Tipos de Argumentos

Cuando se invoca un método de módulo nativo en JavaScript, React Native convierte los argumentos de objetos JS a sus análogos de objetos Java/Kotlin. Por ejemplo, si tu método de módulo nativo Java acepta un double, en JS debes llamar al método con un número. React Native manejará la conversión por ti. A continuación hay una lista de los tipos de argumentos admitidos para métodos de módulos nativos y sus equivalentes en JavaScript.

JavaKotlinJavaScript
BooleanBoolean?boolean
booleanboolean
DoubleDouble?number
doublenumber
StringStringstring
CallbackCallbackFunction
PromisePromisePromise
ReadableMapReadableMapObject
ReadableArrayReadableArrayArray
información

Los siguientes tipos son compatibles actualmente pero no lo serán en TurboModules. Evita usarlos:

  • Integer Java/Kotlin -> ?number
  • Float Java/Kotlin -> ?number
  • int Java -> number
  • float Java -> number

Para tipos de argumentos no listados arriba, deberás manejar la conversión tú mismo. Por ejemplo, en Android, la conversión de Date no es compatible de forma nativa. Puedes manejar la conversión al tipo Date dentro del método nativo así:

java
    String dateFormat = "yyyy-MM-dd";
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
Calendar eStartDate = Calendar.getInstance();
try {
eStartDate.setTime(sdf.parse(startDate));
}

Exportar constantes

Un módulo nativo puede exportar constantes implementando el método nativo getConstants(), disponible en JavaScript. A continuación implementarás getConstants() y devolverás un Map que contiene la constante DEFAULT_EVENT_NAME accesible desde JavaScript:

java
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put("DEFAULT_EVENT_NAME", "New Event");
return constants;
}

La constante puede entonces accederse invocando getConstants en el módulo nativo desde JS:

tsx
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);

Técnicamente es posible acceder a constantes exportadas en getConstants() directamente desde el objeto del módulo nativo. Esto dejará de ser compatible con TurboModules, por lo que recomendamos migrar al enfoque anterior para evitar futuras refactorizaciones.

nota

Actualmente las constantes se exportan solo durante la inicialización, por lo que modificar valores de getConstants en tiempo de ejecución no afectará el entorno JavaScript. Esto cambiará con Turbomodules: getConstants() se convertirá en un método nativo regular donde cada invocación accederá al lado nativo.

Callbacks

Los módulos nativos también soportan un tipo especial de argumento: los callbacks. Se usan para pasar datos de Java/Kotlin a JavaScript en métodos asíncronos. También pueden ejecutar JavaScript asíncronamente desde el lado nativo.

Para crear un método de módulo nativo con callback, primero importa la interfaz Callback, luego añade un nuevo parámetro de tipo Callback a tu método. Hay matices con los callbacks que pronto cambiarán con TurboModules. Actualmente: solo puedes tener dos callbacks en los argumentos (éxito y error), y el último argumento será interpretado como callback de éxito si es función, mientras el penúltimo será de error.

java
import com.facebook.react.bridge.Callback;

@ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
}

Puedes invocar el callback en tu método Java/Kotlin, proporcionando cualquier dato serializable para JavaScript (usa WriteableMaps para objetos nativos o WritableArrays para colecciones). Importante: el callback no se invoca inmediatamente al completar la función nativa. Abajo pasamos el ID de un evento creado previamente:

java
  @ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
Integer eventId = ...
callBack.invoke(eventId);
}

Este método luego se accedería desde JavaScript con:

tsx
const onPress = () => {
CalendarModule.createCalendarEvent(
'Party',
'My House',
eventId => {
console.log(`Created a new event with id ${eventId}`);
},
);
};

Detalle crucial: un método nativo solo puede invocar un callback una vez (éxito o error, no ambos). Sin embargo, el módulo nativo puede almacenar el callback e invocarlo más tarde.

Existen dos enfoques para manejo de errores con callbacks:

  1. Seguir la convención de Node: primer argumento del callback es objeto de error.
java
  @ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
Integer eventId = ...
callBack.invoke(null, eventId);
}

En JavaScript, puedes entonces verificar el primer argumento para detectar si se pasó un error:

tsx
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`Error found! ${error}`);
}
console.log(`event id ${eventId} returned`);
},
);
};
  1. Usar callbacks separados (onSuccess/onFailure):
java
@ReactMethod
public void createCalendarEvent(String name, String location, Callback myFailureCallback, Callback mySuccessCallback) {
}

Entonces en JavaScript puedes añadir callbacks separados para respuestas de error y éxito:

tsx
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
error => {
console.error(`Error found! ${error}`);
},
eventId => {
console.log(`event id ${eventId} returned`);
},
);
};

Promesas

Los módulos nativos también pueden resolver Promesas, simplificando tu código JavaScript, especialmente con async/await. Cuando el último parámetro en un método Java/Kotlin es Promise, su equivalente JS devolverá un objeto Promise.

Refactorizar el código anterior para usar promesas en lugar de callbacks luce así:

java
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);
}
}
nota

Similar a los callbacks, un método de módulo nativo puede rechazar o resolver una promesa (pero no ambas cosas) y solo puede hacerlo una vez. Esto significa que puedes llamar un callback de éxito o de fallo (no ambos), y cada callback solo puede invocarse una vez. Sin embargo, un módulo nativo puede almacenar el callback e invocarlo más tarde.

La contraparte en JavaScript de este método devuelve una Promise. Esto significa que puedes usar la palabra clave await dentro de una función asíncrona para llamarlo y esperar su resultado:

tsx
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);
}
};

El método reject acepta diferentes combinaciones de los siguientes argumentos:

java
String code, String message, WritableMap userInfo, Throwable throwable

Para más detalles, puedes encontrar la interfaz Promise.java aquí. Si no se proporciona userInfo, ReactNative lo establecerá como null. Para el resto de parámetros React Native usará un valor predeterminado. El argumento message proporciona el message de error mostrado en la parte superior de una pila de llamadas de error. A continuación se muestra un ejemplo del mensaje de error mostrado en JavaScript a partir de la siguiente llamada de rechazo en Java/Kotlin.

Llamada reject en Java/Kotlin:

java
promise.reject("Create Event error", "Error parsing date", e);

Mensaje de error en la aplicación React Native cuando se rechaza la promise:

Image of error message in React Native app.
Image of error message

Envío de eventos a JavaScript

Los módulos nativos pueden señalar eventos a JavaScript sin ser invocados directamente. Por ejemplo, podrías querer notificar a JavaScript un recordatorio de que un evento del calendario nativo de Android ocurrirá pronto. La forma más sencilla es usar RCTDeviceEventEmitter, que puede obtenerse del ReactContext como en el siguiente fragmento de código.

java
...
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);

Los módulos JavaScript pueden entonces registrarse para recibir eventos mediante addListener en la clase NativeEventEmitter.

tsx
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();
};
}, []);

Obtención de resultados de actividad con startActivityForResult

Necesitarás escuchar onActivityResult si quieres obtener resultados de una actividad iniciada con startActivityForResult. Para ello, debes extender BaseActivityEventListener o implementar ActivityEventListener. Se prefiere el primero por ser más resistente a cambios en la API. Luego, debes registrar el listener en el constructor del módulo así:

java
reactContext.addActivityEventListener(mActivityResultListener);

Ahora puedes escuchar onActivityResult implementando el siguiente método:

java
@Override
public void onActivityResult(
final Activity activity,
final int requestCode,
final int resultCode,
final Intent intent) {
// Your logic here
}

Implementemos un selector básico de imágenes para demostrar esto. El selector expondrá el método pickImage a JavaScript, que devolverá la ruta de la imagen cuando se llame.

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;
}
}
}

Escucha de eventos del ciclo de vida

Escuchar eventos del ciclo de vida de la actividad como onResume, onPause, etc. es muy similar a cómo se implementó ActivityEventListener. El módulo debe implementar LifecycleEventListener. Luego, necesitas registrar un listener en el constructor del módulo así:

java
reactContext.addLifecycleEventListener(this);

Ahora puedes escuchar los eventos del ciclo de vida de la actividad implementando estos métodos:

java
@Override
public void onHostResume() {
// Activity `onResume`
}
@Override
public void onHostPause() {
// Activity `onPause`
}
@Override
public void onHostDestroy() {
// Activity `onDestroy`
}

Manejo de hilos (Threading)

Hasta la fecha en Android, todos los métodos asíncronos de módulos nativos se ejecutan en un único hilo. Los módulos nativos no deben hacer suposiciones sobre en qué hilo se llaman, ya que esta asignación podría cambiar en el futuro. Si se requiere una llamada bloqueante, el trabajo pesado debe delegarse a un hilo de trabajo gestionado internamente, distribuyendo los callbacks desde allí.