Android 原生模块
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →
原生模块(Native Module)和原生组件(Native Components)是旧架构中使用的稳定技术。它们将在新架构稳定后被弃用。新架构采用 Turbo 原生模块 和 Fabric 原生组件 来实现类似功能。
欢迎来到 Android 原生模块指南。请先阅读原生模块介绍了解原生模块的基本概念。
创建日历原生模块
在本指南中,您将创建一个名为 CalendarModule 的原生模块,用于在 JavaScript 中访问 Android 日历 API。最终,您将能够通过 JavaScript 调用 CalendarModule.createCalendarEvent('Dinner Party', 'My House'); 来触发创建日历事件的 Java/Kotlin 方法。
环境准备
首先在 Android Studio 中打开您 React Native 应用内的 Android 工程。React Native 应用中的 Android 工程位于以下路径:

我们推荐使用 Android Studio 来编写原生代码。Android Studio 是专为 Android 开发构建的 IDE,使用它可以帮助你快速解决代码语法错误等小问题。
我们还建议启用 Gradle Daemon 以加速 Java/Kotlin 代码的构建过程。
创建自定义原生模块文件
第一步是在 android/app/src/main/java/com/your-app-name/ 目录下创建 Java/Kotlin 文件(CalendarModule.java 或 CalendarModule.kt)(Kotlin 和 Java 的目录结构相同)。该文件将包含您的原生模块类。

然后添加以下内容:
- 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) {...}
如您所见,CalendarModule 类继承自 ReactContextBaseJavaModule。在 Android 中,Java/Kotlin 原生模块通过扩展 ReactContextBaseJavaModule 并实现 JavaScript 所需功能来实现。
需要注意的是,从技术上讲,Java/Kotlin 类只需继承 BaseJavaModule 类或实现 NativeModule 接口即可被 React Native 视为原生模块。
但我们推荐使用如上所示的 ReactContextBaseJavaModule。ReactContextBaseJavaModule 提供了对 ReactApplicationContext(RAC)的访问权限,这对于需要挂钩 Activity 生命周期方法的原生模块非常有用。使用 ReactContextBaseJavaModule 还能让您的原生模块在未来更易实现类型安全。在即将发布的版本中,为了实现原生模块类型安全,React Native 会检查每个原生模块的 JavaScript 规范,并生成继承自 ReactContextBaseJavaModule 的抽象基类。
模块名称
所有 Android 的 Java/Kotlin 原生模块都必须实现 getName() 方法。该方法返回表示原生模块名称的字符串,JavaScript 可通过此名称访问该模块。例如在以下代码片段中,getName() 返回 "CalendarModule"。
- Java
- Kotlin
// add to CalendarModule.java
@Override
public String getName() {
return "CalendarModule";
}
// add to CalendarModule.kt
override fun getName() = "CalendarModule"
这样 JavaScript 中就可以这样访问原生模块:
const {CalendarModule} = ReactNative.NativeModules;
向 JavaScript 暴露原生方法
接下来您需要添加一个用于创建日历事件的方法,并使其可在 JavaScript 中调用。所有需要从 JavaScript 调用的原生模块方法都必须使用 @ReactMethod 注解。
为 CalendarModule 设置 createCalendarEvent() 方法,使其可通过 CalendarModule.createCalendarEvent() 在 JS 中调用。目前该方法将接收名称和位置两个字符串参数,参数类型选项将在稍后介绍。
- Java
- Kotlin
@ReactMethod
public void createCalendarEvent(String name, String location) {
}
@ReactMethod fun createCalendarEvent(name: String, location: String) {}
在方法中添加调试日志,用于确认从应用调用时是否触发。以下是导入和使用 Android util 包中 Log 类的示例:
- 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")
}
完成原生模块实现并在 JavaScript 中连接后,您可以按照这些步骤查看应用日志。
同步方法
你可以给原生方法传递 isBlockingSynchronousMethod = true 参数,将其标记为同步方法。
- Java
- Kotlin
@ReactMethod(isBlockingSynchronousMethod = true)
@ReactMethod(isBlockingSynchronousMethod = true)
目前我们不推荐这种做法,因为同步调用方法可能导致严重的性能损失,并给原生模块引入线程相关的错误。另外请注意,如果启用 isBlockingSynchronousMethod,应用将无法再使用 Google Chrome 调试器。这是因为同步方法要求 JS 虚拟机与应用共享内存,而在 Google Chrome 调试器中,React Native 实际运行在 Chrome 的 JS 虚拟机里,通过 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/ 目录下创建实现 ReactPackage 的 Java/Kotlin 类(命名为 MyAppPackage.java 或 MyAppPackage.kt):
然后添加以下内容:
- 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()
}
该文件导入了你创建的 CalendarModule 原生模块,在 createNativeModules() 函数中实例化 CalendarModule,并将其作为 NativeModules 列表返回注册。后续添加更多原生模块时,也可在此实例化并加入返回列表。
需要注意的是,这种注册方式会在应用启动时立即初始化所有原生模块,从而增加应用的启动时间。您可以使用 TurboReactPackage 作为替代方案。与返回已实例化原生模块对象列表的 createNativeModules 不同,TurboReactPackage 实现了按需创建原生模块对象的 getModule(String name, ReactApplicationContext rac) 方法。目前 TurboReactPackage 的实现稍显复杂:除了实现 getModule() 方法,您还必须实现 getReactModuleInfoProvider() 方法,该方法返回包可实例化的所有原生模块列表及其实例化函数(示例参见此处)。再次强调,使用 TurboReactPackage 能加快应用启动速度,但目前编写较为繁琐。因此若选择使用 TurboReactPackage,请谨慎行事。
要注册 CalendarModule 包,需将 MyAppPackage 添加到 ReactNativeHost 的 getPackages() 方法返回列表中。打开位于 android/app/src/main/java/com/your-app-name/ 的 MainApplication.java 或 MainApplication.kt 文件:
找到 ReactNativeHost 的 getPackages() 方法,将你的包添加到 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())
}
现在你已成功为 Android 注册了原生模块!
测试构建结果
至此你已完成 Android 原生模块的基础搭建。现在通过 JavaScript 访问原生模块并调用其导出方法进行测试。
在应用中选定调用原生模块 createCalendarEvent() 的位置。以下示例组件 NewModuleButton 可添加到应用中,在 NewModuleButton 的 onPress() 函数内调用原生模块:
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;
要从 JavaScript 访问原生模块,首先需要从 React Native 导入 NativeModules:
import {NativeModules} from 'react-native';
然后即可通过 NativeModules 访问 CalendarModule 原生模块。
const {CalendarModule} = NativeModules;
现在 CalendarModule 原生模块已可用,您可以直接调用原生方法 createCalendarEvent()。以下示例将其添加到 NewModuleButton 的 onPress() 方法中:
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};
最后一步是重新构建 React Native 应用,以获取包含新原生模块的最新原生代码。在 React Native 应用所在命令行中执行:
- npm
- Yarn
npm run android
yarn android
迭代开发时的构建
在按照指南开发并迭代原生模块时,您需要通过原生构建来获取 JavaScript 的最新变更。因为您编写的代码属于应用的原生部分。虽然 React Native 的 Metro 打包器可以监听 JavaScript 变更并实时重建,但这不适用于原生代码。因此要测试最新的原生变更,需要使用上述命令重新构建。
回顾 ✨
现在您应该能在应用中通过原生模块调用 createCalendarEvent() 方法了(本示例中通过点击 NewModuleButton 触发)。可以通过查看您在 createCalendarEvent() 方法中设置的日志来确认调用成功。请遵循 这些步骤 查看 ADB 日志。随后搜索 Log.d 消息(本示例中为"Create event called with name: testName and location: testLocation"),每次调用原生方法时都应看到该日志。

至此您已完成 Android 原生模块的创建,并在 React Native 应用中通过 JavaScript 调用其原生方法。接下来可继续了解原生模块方法的参数类型、回调函数和 Promise 配置等进阶内容。
日历模块的进阶应用
优化模块导出方式
通过 NativeModules 导入原生模块的方式略显繁琐。
为避免每次使用都需重复此操作,可为模块创建 JavaScript 封装层。新建名为 CalendarModule.js 的 JavaScript 文件并添加以下内容:
/**
* 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;
该 JavaScript 文件也是添加 JavaScript 端功能的理想位置。例如使用 TypeScript 等类型系统时,可在此处添加类型注解。虽然 React Native 暂不支持原生到 JS 的类型安全,但所有 JS 代码均可获得类型保障。这也为将来迁移到类型安全的原生模块奠定基础。以下是添加类型安全的示例:
/**
* 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;
在其他 JavaScript 文件中可如此访问原生模块并调用其方法:
import CalendarModule from './CalendarModule';
CalendarModule.createCalendarEvent('foo', 'bar');
此说明假设您导入 CalendarModule 的位置与 CalendarModule.js 处于相同层级,请根据实际情况调整相对导入路径。
参数类型
当在 JavaScript 中调用原生模块方法时,React Native 会将参数从 JS 对象转换为对应的 Java/Kotlin 对象。例如若 Java 原生方法接收 double 类型,在 JS 中调用时需传入 number 类型。React Native 会自动处理类型转换。下表列出原生模块方法支持的参数类型及其对应的 JavaScript 类型:
| 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 |
以下类型当前受支持,但在 TurboModules 中将不再支持,请避免使用:
- Integer Java/Kotlin -> ?number
- Float Java/Kotlin -> ?number
- int Java -> number
- float Java -> number
对于上表未列出的参数类型,需自行处理转换。例如 Android 中 Date 类型不直接支持转换,可在原生方法内手动处理转换到 Date 类型:
- 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
}
}
导出常量
原生模块可以通过实现 getConstants() 原生方法来导出常量,这些常量在 JavaScript 中可用。下面将实现 getConstants() 并返回一个包含 DEFAULT_EVENT_NAME 常量的 Map,该常量可在 JavaScript 中访问:
- 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")
然后在 JS 中可通过调用原生模块的 getConstants 方法访问该常量:
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);
从技术上讲,目前可以直接通过原生模块对象访问 getConstants() 导出的常量。但在 TurboModules 中不再支持此方式,因此我们建议开发者采用上述方式以避免后续不必要的迁移。
请注意:常量仅在初始化阶段导出,因此运行时修改 getConstants 返回值不会影响 JavaScript 环境。此行为将在 Turbomodules 中改变——届时 getConstants() 将成为常规原生模块方法,每次调用都会访问原生端。
回调函数
原生模块还支持一种特殊的参数:回调函数。回调用于在异步方法中将数据从 Java/Kotlin 传递到 JavaScript,也可用于从原生端异步执行 JavaScript。
要创建带回调的原生模块方法,首先导入 Callback 接口,然后在原生模块方法中添加类型为 Callback 的新参数。回调参数有两个需要注意的特性(这些特性将在 TurboModules 中调整):首先,函数参数中最多只能有两个回调——successCallback 和 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) {}
您可以在 Java/Kotlin 方法中调用回调,传递任何想要发送到 JavaScript 的数据。请注意,只能传递可序列化数据。如需传递原生对象可使用 WriteableMaps,集合数据可使用 WritableArrays。重点强调:回调不会在原生函数完成后立即调用。下面将之前调用创建的事件ID传递给回调。
- 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)
}
然后在 JavaScript 中通过以下方式调用该方法:
const onPress = () => {
CalendarModule.createCalendarEvent(
'Party',
'My House',
eventId => {
console.log(`Created a new event with id ${eventId}`);
},
);
};
另一个重要细节:原生模块方法只能调用一次回调(无论是成功还是失败回调),且每个回调最多只能调用一次。但原生模块可以存储回调并在稍后调用。
回调的错误处理有两种常用方式。第一种遵循 Node 规范,将回调的第一个参数视为错误对象:
- 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)
}
在 JavaScript 中可检查第一个参数判断是否传递了错误:
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`Error found! ${error}`);
}
console.log(`event id ${eventId} returned`);
},
);
};
另一种方式是使用独立的 onSuccess 和 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
) {}
然后在 JavaScript 中为错误和成功响应分别添加回调:
const onPress = () => {
CalendarModule.createCalendarEvent(
'testName',
'testLocation',
error => {
console.error(`Error found! ${error}`);
},
eventId => {
console.log(`event id ${eventId} returned`);
},
);
};
Promise
原生模块也可以返回 Promise,这能简化 JavaScript 代码,特别是在使用 ES2016 的 async/await 语法时。当 Java/Kotlin 方法的最后一个参数是 Promise 时,其对应的 JS 方法将返回 JS Promise 对象。
将上述代码从回调重构为使用 Promise 的示例如下:
- 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)
}
}
与回调类似,原生模块方法可以拒绝 (reject) 或解决 (resolve) Promise(但不能同时进行),且最多执行一次。这意味着您只能调用成功回调或失败回调之一(不可同时调用),且每个回调最多触发一次。但原生模块可以存储回调并在稍后调用。
该方法的 JavaScript 对应版本会返回一个 Promise。这意味着你可以在异步函数中使用 await 关键字调用它并等待结果:
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);
}
};
reject 方法接受以下参数的任意组合:
- Java
- Kotlin
String code, String message, WritableMap userInfo, Throwable throwable
code: String, message: String, userInfo: WritableMap, throwable: Throwable
更多细节请参见 Promise.java 接口。如果未提供 userInfo,React Native 会将其设为 null。其余参数 React Native 将使用默认值。message 参数提供错误调用堆栈顶部显示的错误 message。以下是 Java/Kotlin 中 reject 调用在 JavaScript 端显示的错误信息示例。
Java/Kotlin 的 reject 调用:
- Java
- Kotlin
promise.reject("Create Event error", "Error parsing date", e);
promise.reject("Create Event error", "Error parsing date", e)
Promise 被拒绝时在 React Native 应用中显示的错误信息:

向 JavaScript 发送事件
原生模块无需被直接调用即可向 JavaScript 发出事件信号。例如,你可能希望向 JavaScript 发出提醒:原生 Android 日历应用中的某个日历事件即将发生。最简单的方式是使用 RCTDeviceEventEmitter,可通过 ReactContext 获取,如下所示:
- 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)
JavaScript 模块随后可以通过 NativeEventEmitter 类的 addListener 方法注册接收事件:
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();
};
}, []);
从 startActivityForResult 获取 Activity 结果
若要从通过 startActivityForResult 启动的 Activity 获取结果,需要监听 onActivityResult。为此,必须扩展 BaseActivityEventListener 或实现 ActivityEventListener。建议选择前者,因其更能适应 API 变更。接着在模块构造函数中注册监听器:
- Java
- Kotlin
reactContext.addActivityEventListener(mActivityResultListener);
reactContext.addActivityEventListener(mActivityResultListener);
现在可以通过实现以下方法监听 onActivityResult:
- 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
}
让我们实现一个基础图片选择器来演示此功能。该选择器将向 JavaScript 暴露 pickImage 方法,调用时会返回图片路径:
- 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"
}
}
监听生命周期事件
监听 Activity 的生命周期事件(如 onResume, onPause 等)与实现 ActivityEventListener 类似。模块必须实现 LifecycleEventListener,然后在模块构造函数中注册监听器:
- Java
- Kotlin
reactContext.addLifecycleEventListener(this);
reactContext.addLifecycleEventListener(this)
现在可以通过实现以下方法监听 Activity 的生命周期事件:
- 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`
}
线程处理
目前,在 Android 上所有原生模块的异步方法都在单一线程执行。原生模块不应预设其被调用的线程类型,因为当前分配方案未来可能变更。若需执行阻塞调用,应将繁重任务分派到内部管理的工作线程,并从该线程分发回调。