跳到主要内容

Android 原生模块

信息

Native Module 和 Native Components 是我们在旧架构中使用的稳定技术。 当 New Architecture 变得稳定时,它们将在未来被弃用。New Architecture 使用 Turbo Native ModuleFabric Native Components 来实现类似的结果。

欢迎阅读 Android 原生模块。请先阅读 原生模块简介,了解原生模块是什么。

创建一个日历原生模块

在下面的指南中,你将创建一个原生模块 CalendarModule,它将允许你从 JavaScript 中访问 Android 的日历 API。到最后,你将能够从 JavaScript 中调用 CalendarModule.createCalendarEvent('Dinner Party', 'My House');,从而执行一个创建日历事件的 Java/Kotlin 方法。

设置

开始之前,请在 Android Studio 中打开你的 React Native 应用内的 Android 项目。你可以在一个 React Native 应用中在这里找到你的 Android 项目:

在 Android Studio 中打开 React Native 应用内的 Android 项目的图片。
你可以在这里找到你的 Android 项目的位置

我们建议使用 Android Studio 编写原生代码。Android Studio 是为 Android 开发构建的 IDE,使用它将帮助你快速解决诸如代码语法错误之类的小问题。

我们还建议启用 Gradle Daemon,以便在你迭代 Java/Kotlin 代码时加快构建速度。

创建一个自定义原生模块文件

第一步是在 android/app/src/main/java/com/your-app-name/ 文件夹中创建 (CalendarModule.javaCalendarModule.kt) Java/Kotlin 文件(该文件夹对 Kotlin 和 Java 都相同)。这个 Java/Kotlin 文件将包含你的原生模块 Java/Kotlin 类。

在 Android Studio 中添加名为 CalendarModule.java 的类的图片。
如何添加 CalendarModule 类的示意图

然后添加以下内容:

Java
package com.your-apps-package-name; // 将 your-apps-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);
}
}

如你所见,你的 CalendarModule 类继承了 ReactContextBaseJavaModule 类。对于 Android,Java/Kotlin 原生模块是作为扩展 ReactContextBaseJavaModule 并实现 JavaScript 所需功能的类来编写的。

备注

值得注意的是,从技术上讲,Java/Kotlin 类只需要扩展 BaseJavaModule 类或实现 NativeModule 接口,就可以被 React Native 视为原生模块。

不过我们建议你像上面所示一样使用 ReactContextBaseJavaModuleReactContextBaseJavaModule 提供对 ReactApplicationContext(RAC)的访问,这对于需要挂接到 activity 生命周期方法的原生模块很有用。使用 ReactContextBaseJavaModule 也会让你在未来更容易让原生模块具备类型安全性。对于即将在未来版本中推出的原生模块类型安全,React Native 会查看每个原生模块的 JavaScript 规范,并生成一个扩展 ReactContextBaseJavaModule 的抽象基类。

模块名称

Android 中所有 Java/Kotlin 原生模块都需要实现 getName() 方法。该方法返回一个字符串,表示原生模块的名称。之后可以在 JavaScript 中通过这个名称访问该原生模块。例如,在下面的代码片段中,getName() 返回 "CalendarModule"

Java
// 添加到 CalendarModule.java
@Override
public String getName() {
return "CalendarModule";
}

然后就可以在 JS 中像这样访问该原生模块:

React TSX
const {CalendarModule} = ReactNative.NativeModules;

向 JavaScript 导出一个原生方法

接下来,你需要向原生模块中添加一个方法,该方法将创建日历事件,并且可以在 JavaScript 中调用。所有旨在从 JavaScript 中调用的原生模块方法都必须使用 @ReactMethod 注解。

CalendarModule 设置一个 createCalendarEvent() 方法,使其可以通过 CalendarModule.createCalendarEvent() 在 JS 中被调用。目前,该方法将接收名称和位置两个字符串参数。参数类型选项很快会介绍。

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

在方法中添加一条调试日志,以确认你从应用中调用它时它已被执行。下面是一个如何从 Android util 包中导入并使用 Log 类的示例:

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

完成原生模块的实现并将其接入 JavaScript 后,你可以按照 这些步骤 查看应用中的日志。

同步方法

你可以向原生方法传入 isBlockingSynchronousMethod = true,将其标记为同步方法。

Java
@ReactMethod(isBlockingSynchronousMethod = true)

目前我们不建议这样做,因为同步调用方法可能会带来严重的性能损耗,并给你的原生模块引入与线程相关的 bug。此外,请注意,如果你选择启用 isBlockingSynchronousMethod,你的应用将无法再使用 Google Chrome 调试器。这是因为同步方法要求 JS VM 与应用共享内存。对于 Google Chrome 调试器,React Native 在 Google Chrome 的 JS VM 中运行,并通过 WebSockets 与移动设备异步通信。

注册模块(Android 特定)

一旦编写好原生模块,就需要将其注册到 React Native 中。为此,你需要将原生模块添加到 ReactPackage 中,并将该 ReactPackage 注册到 React Native。初始化期间,React Native 会遍历所有包,并为每个 ReactPackage 注册其中的每个原生模块。

React Native 会在一个 ReactPackage 上调用 createNativeModules() 方法,以获取要注册的原生模块列表。对于 Android,如果某个模块没有在 createNativeModules 中实例化并返回,那么它将无法从 JavaScript 中使用。

要将你的原生模块添加到 ReactPackage,首先在 android/app/src/main/java/com/your-app-name/ 文件夹内创建一个名为 (MyAppPackage.javaMyAppPackage.kt) 的新 Java/Kotlin 类,并实现 ReactPackage

然后添加以下内容:

Java
package com.your-app-name; // 将 your-app-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;
}

}

这个文件导入了你创建的原生模块 CalendarModule。然后它在 createNativeModules() 函数中实例化 CalendarModule,并将其作为 NativeModules 列表返回以供注册。如果你之后添加更多原生模块,也可以在这里实例化它们并将其添加到返回的列表中。

备注

值得注意的是,这种注册原生模块的方式会在应用启动时急切地初始化所有原生模块,这会增加应用的启动时间。你可以使用 TurboReactPackage 作为替代。与返回已实例化原生模块对象列表的 createNativeModules 不同,TurboReactPackage 实现了一个 getModule(String name, ReactApplicationContext rac) 方法,在需要时创建原生模块对象。TurboReactPackage 目前实现起来稍微复杂一些。除了实现 getModule() 方法外,你还必须实现 getReactModuleInfoProvider() 方法,该方法会返回包可以实例化的所有原生模块列表,以及一个用于实例化它们的函数,示例如这里。同样,使用 TurboReactPackage 可以让你的应用拥有更快的启动时间,但目前编写起来稍显繁琐。因此,如果你选择使用 TurboReactPackages,请谨慎行事。

要注册 CalendarModule 包,你必须将 MyAppPackage 添加到 ReactNativeHost 的 getPackages() 方法返回的包列表中。打开你的 MainApplication.javaMainApplication.kt 文件,它可以在以下路径中找到:android/app/src/main/java/com/your-app-name/

找到 ReactNativeHost 的 getPackages() 方法,并将你的包添加到 getPackages() 返回的包列表中:

Java
@Override
protected List<ReactPackage> getPackages() {
List<ReactPackage> packages = new PackageList(this).getPackages();
// 目前还不能自动链接的包可以在这里手动添加,例如:
// packages.add(new MyReactNativePackage());
packages.add(new MyAppPackage());
return packages;
}

你现在已经成功为 Android 注册了你的原生模块!

测试你所构建的内容

到目前为止,你已经在 Android 中为你的原生模块设置好了基本框架。通过访问原生模块并在 JavaScript 中调用其导出的方法来进行测试。

在你的应用中找一个你想要添加对原生模块 createCalendarEvent() 方法调用的位置。下面是一个组件示例,NewModuleButton,你可以将其添加到应用中。你可以在 NewModuleButtononPress() 函数中调用原生模块。

React TSX
import {NativeModules, Button} from 'react-native';

const NewModuleButton = () => {
const onPress = () => {
console.log('我们将在这里调用原生模块!');
};

return (
<Button
title="点击调用你的原生模块!"
color="#841584"
onPress={onPress}
/>
);
};

export default NewModuleButton;

要从 JavaScript 中访问你的原生模块,你需要先从 React Native 导入 NativeModules

React TSX
import {NativeModules} from 'react-native';

然后你就可以从 NativeModules 中访问 CalendarModule 原生模块。

React TSX
const {CalendarModule} = NativeModules;

现在你已经可以使用 CalendarModule 原生模块了,你可以调用你的原生方法 createCalendarEvent()。下面是将其添加到 NewModuleButton 中的 onPress() 方法后的示例:

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

最后一步是重新构建 React Native 应用,这样你就可以使用最新的原生代码(以及你新的原生模块!)。在你的命令行中,也就是 React Native 应用所在的位置,运行以下命令:

npm run android

在迭代过程中进行构建

当你按照这些指南进行开发并迭代你的原生模块时,你需要对应用进行原生重新构建,才能从 JavaScript 中访问最新的更改。这是因为你编写的代码位于应用的原生部分。虽然 React Native 的 metro 打包器可以监控 JavaScript 的更改并在运行时为你重建,但它不会对原生代码这样做。所以如果你想测试最新的原生更改,就需要使用上面的命令重新构建。

回顾✨

现在你应该能够在应用中调用原生模块上的 createCalendarEvent() 方法了。在我们的示例中,这是通过按下 NewModuleButton 来实现的。你可以通过查看你在 createCalendarEvent() 方法中设置的日志来确认这一点。你可以按照 这些步骤 在应用中查看 ADB 日志。然后你应该能够搜索你的 Log.d 消息(在我们的示例中为 “Create event called with name: testName and location: testLocation”),并看到每次调用原生模块方法时都会记录该消息。

Image of logs.
Android Studio 中 ADB 日志的示意图

到目前为止,你已经创建了一个 Android 原生模块,并在 React Native 应用中从 JavaScript 调用了它的原生方法。你可以继续阅读,了解更多内容,例如原生模块方法可用的参数类型,以及如何设置回调和 promise。

超越日历原生模块

更好的原生模块导出

像上面那样通过从 NativeModules 中取出你的原生模块来导入它,多少有点笨拙。

为了让你的原生模块使用者不必每次访问你的原生模块时都这么做,你可以为该模块创建一个 JavaScript 包装器。创建一个名为 CalendarModule.js 的新 JavaScript 文件,内容如下:

React TSX
/**
* 这会将原生的 CalendarModule 模块作为一个 JS 模块暴露出来。它有一个
* 函数 'createCalendarEvent',接收以下参数:

* 1. String name: 表示事件名称的字符串
* 2. String location: 表示事件地点的字符串
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
export default CalendarModule;

这个 JavaScript 文件也会成为你添加任何 JavaScript 侧功能的好位置。例如,如果你使用像 TypeScript 这样的类型系统,你可以在这里为你的原生模块添加类型注解。虽然 React Native 目前还不支持 Native 到 JS 的类型安全,但你所有的 JS 代码都可以是类型安全的。这样做还会让你将来更容易切换到类型安全的原生模块。下面是为 CalendarModule 添加类型安全的示例:

React TSX
/**
* 这会将原生的 CalendarModule 模块作为一个 JS 模块暴露出来。它有一个
* 函数 'createCalendarEvent',接收以下参数:
*
* 1. String name: 表示事件名称的字符串
* 2. String location: 表示事件地点的字符串
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
interface CalendarInterface {
createCalendarEvent(name: string, location: string): void;
}
export default CalendarModule as CalendarInterface;

在你的其他 JavaScript 文件中,你可以像这样访问原生模块并调用它的方法:

React TSX
import CalendarModule from './CalendarModule';
CalendarModule.createCalendarEvent('foo', 'bar');
备注

这假设你导入 CalendarModule 的位置与 CalendarModule.js 处于同一层级结构中。请根据需要更新相对导入路径。

参数类型

当在 JavaScript 中调用原生模块方法时,React Native 会把参数从 JS 对象转换为它们对应的 Java/Kotlin 对象。例如,如果你的 Java 原生模块方法接受一个 double,那么在 JS 中你需要用一个 number 来调用该方法。React Native 会帮你处理这个转换。下面是原生模块方法支持的参数类型及其对应的 JavaScript 类型列表。

JavaKotlinJavaScript
BooleanBoolean?boolean
booleanboolean
DoubleDouble?number
doublenumber
StringStringstring
CallbackCallbackFunction
PromisePromisePromise
ReadableMapReadableMapObject
ReadableArrayReadableArrayArray
信息

以下类型目前受支持,但在 TurboModules 中将不再支持。请避免使用它们:

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

对于上面未列出的参数类型,你需要自己处理转换。例如,在 Android 中,Date 的转换默认不受支持。你可以在原生方法内部自行将其转换为 Date 类型,如下所示:

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

导出常量

原生模块可以通过实现原生方法 getConstants() 来导出常量,该方法可在 JS 中使用。下面你将实现 getConstants() 并返回一个包含 DEFAULT_EVENT_NAME 常量的 Map,你可以在 JavaScript 中访问它:

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

然后可以通过在 JS 中对原生模块调用 getConstants 来访问该常量:

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

从技术上讲,可以直接从原生模块对象上访问 getConstants() 中导出的常量。但 TurboModules 将不再支持这种方式,因此我们建议社区切换到上面的做法,以避免日后需要迁移。

备注

目前常量只会在初始化时导出,因此如果你在运行时更改 getConstants 的值,不会影响 JavaScript 环境。TurboModules 会改变这一点。使用 TurboModules 时,getConstants() 将成为一个常规的原生模块方法,每次调用都会触达原生侧。

回调

原生模块也支持一种特殊的参数:回调。回调用于将数据从 Java/Kotlin 传递给 JavaScript,用于异步方法。它们也可以用于从原生侧异步执行 JavaScript。

要创建一个带回调的原生模块方法,首先导入 Callback 接口,然后为你的原生模块方法添加一个类型为 Callback 的新参数。回调参数有一些细微差别,这些差别在 TurboModules 中很快会被消除。首先,你的函数参数中只能有两个回调——一个 successCallback 和一个 failureCallback。另外,如果原生模块方法调用的最后一个参数是函数,则它会被视为 successCallback;如果原生模块方法调用的倒数第二个参数是函数,则它会被视为 failure callback。

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

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

你可以在 Java/Kotlin 方法中调用回调,传入你想传递给 JavaScript 的任何数据。请注意,你只能从原生代码向 JavaScript 传递可序列化的数据。如果你需要传回一个原生对象,可以使用 WriteableMaps;如果你需要使用集合,请使用 WritableArrays。还需要强调的是,回调不会在原生函数完成后立即被调用。下面会把在前一次调用中创建的事件 ID 传递给回调。

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

然后可以在 JavaScript 中这样访问此方法:

React TSX
const onPress = () => {
CalendarModule.createCalendarEvent(
'Party',
'My House',
eventId => {
console.log(`已创建一个新事件,id 为 ${eventId}`);
},
);
};

另一个需要注意的重要细节是,原生模块方法一次只能调用一个回调,而且只能调用一次。这意味着你可以调用 success callback 或 failure callback,但不能同时调用,而且每个回调最多只能被调用一次。不过,原生模块可以存储回调,并在稍后再调用它。

使用回调进行错误处理有两种方式。第一种是遵循 Node 的惯例,将传给回调的第一个参数视为错误对象。

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

在 JavaScript 中,你可以检查第一个参数,看看是否传入了错误:

React TSX
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`发现错误!${error}`);
}
console.log(`返回的事件 id ${eventId}`);
},
);
};

另一种选择是使用 onSuccess 和 onFailure 回调:

Java
@ReactMethod
public void createCalendarEvent(String name, String location, Callback myFailureCallback, Callback mySuccessCallback) {
}

然后在 JavaScript 中,你可以为错误和成功响应分别添加单独的回调:

React TSX
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
error => {
console.error(`发现错误!${error}`);
},
eventId => {
console.log(`返回的事件 id ${eventId}`);
},
);
};

Promise

原生模块也可以返回一个 Promise,这可以简化你的 JavaScript,尤其是在使用 ES2016 的 async/await 语法时。当原生模块 Java/Kotlin 方法的最后一个参数是 Promise 时,对应的 JS 方法将返回一个 JS Promise 对象。

将上面的代码重构为使用 promise 而不是回调,看起来如下:

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);
}
}
备注

与回调类似,原生模块方法要么拒绝,要么解决一个 promise(但不能同时进行),且最多只能这样做一次。这意味着你可以调用 success callback 或 failure callback,但不能同时调用,而且每个回调最多只能被调用一次。不过,原生模块可以存储回调,并在稍后再调用它。

该方法对应的 JavaScript 版本会返回一个 Promise。这意味着你可以在 async 函数中使用 await 关键字来调用它并等待其结果:

React TSX
const onSubmit = async () => {
try {
const eventId = await CalendarModule.createCalendarEvent(
'Party',
'My House',
);
console.log(`已创建一个新事件,id 为 ${eventId}`);
} catch (e) {
console.error(e);
}
};

reject 方法可以接受以下参数的不同组合:

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

更多细节可以在这里找到 Promise.java 接口:这里。如果未提供 userInfo,ReactNative 会将其设为 null。对于其余参数,React Native 会使用默认值。message 参数提供错误 message,它会显示在错误调用栈顶部。下面是 Java/Kotlin 中以下 reject 调用在 JavaScript 中显示的错误信息示例。

Java/Kotlin reject 调用:

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

当 promise 被拒绝时,React Native App 中的错误信息:

React Native 应用中的错误信息图片。
错误信息图片

向 JavaScript 发送事件

原生模块可以在不被直接调用的情况下向 JavaScript 发出事件。例如,你可能希望向 JavaScript 发出一个提醒:原生 Android 日历应用中的某个日历事件即将发生。最简单的方法是使用 RCTDeviceEventEmitter,它可以像下面代码片段中那样从 ReactContext 中获取。

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) {
// 根据需要设置任何上游监听器或后台任务
}

listenerCount += 1;
}

@ReactMethod
public void removeListeners(Integer count) {
listenerCount -= count;
if (listenerCount == 0) {
// 移除上游监听器,停止不必要的后台任务
}
}
...
WritableMap params = Arguments.createMap();
params.putString("eventProperty", "someValue");
...
sendEvent(reactContext, "EventReminder", params);

然后 JavaScript 模块可以通过在 NativeEventEmitter 类上调用 addListener 来注册接收事件。

React 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"
});

// 卸载时移除监听器
return () => {
eventListener.remove();
};
}, []);

从 startActivityForResult 获取 Activity 结果

如果你想从使用 startActivityForResult 启动的 activity 中获取结果,就需要监听 onActivityResult。为此,你必须继承 BaseActivityEventListener 或实现 ActivityEventListener。前者更推荐,因为它对 API 变化更具弹性。然后,你需要像这样在模块构造函数中注册监听器:

Java
reactContext.addActivityEventListener(mActivityResultListener);

现在你可以通过实现以下方法来监听 onActivityResult

Java
@Override
public void onActivityResult(
final Activity activity,
final int requestCode,
final int resultCode,
final Intent intent) {
// 你的逻辑在这里
}

让我们通过实现一个基础的图片选择器来演示这一点。这个图片选择器会向 JavaScript 暴露 pickImage 方法,调用后会返回图片的路径。

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

// 为 `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;
}

// 存储 promise,以便在选择器返回数据时进行 resolve/reject
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;
}
}
}

监听生命周期事件

监听 activity 的生命周期事件,例如 onResumeonPause 等,与实现 ActivityEventListener 的方式非常相似。模块必须实现 LifecycleEventListener。然后,你需要像这样在模块构造函数中注册一个监听器:

Java
reactContext.addLifecycleEventListener(this);

现在你可以通过实现以下方法来监听 activity 的生命周期事件:

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

线程

截至目前,在 Android 上,所有原生模块的异步方法都在同一个线程上执行。原生模块不应对它们被调用时所在线程作任何假设,因为当前的分配方式未来可能会改变。如果需要阻塞调用,繁重的工作应派发到内部管理的工作线程中,并从那里分发任何回调。