Android 原生模組
原生模組和原生組件是我們的穩定技術,舊版架構使用它們。當新架構穩定後,它們將在未來被棄用。新架構使用 Turbo 原生模組 和 Fabric 原生組件 來達成類似的結果。
歡迎來到 Android 原生模組。請先閱讀 原生模組簡介,以了解原生模組是什麼。
建立日曆原生模組
在以下指南中,您將建立一個名為 CalendarModule
的原生模組,讓您可以從 JavaScript 存取 Android 的日曆 API。完成後,您將能夠從 JavaScript 呼叫 CalendarModule.createCalendarEvent('晚餐派對', '我家');
,調用一個建立日曆事件的 Java/Kotlin 方法。
設定
首先,在 Android Studio 中開啟 React Native 應用程式內的 Android 專案。您可以在 React Native 應用程式中的這裡找到您的 Android 專案

我們建議使用 Android Studio 來編寫您的原生程式碼。Android Studio 是一個專為 Android 開發而建構的 IDE,使用它將幫助您快速解決程式碼語法錯誤等小問題。
我們也建議啟用 Gradle 守護進程,以加快您迭代 Java/Kotlin 程式碼時的建置速度。
建立自訂原生模組檔案
第一步是在 android/app/src/main/java/com/your-app-name/
資料夾(Kotlin 和 Java 的資料夾相同)內建立 (CalendarModule.java
或 CalendarModule.kt
) Java/Kotlin 檔案。此 Java/Kotlin 檔案將包含您的原生模組 Java/Kotlin 類別。

然後新增以下內容
- 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),這對於需要 Hook 到 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"
然後可以像這樣在 JS 中存取原生模組
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 中 Hook 它之後,您可以按照這些步驟來檢視您應用程式的日誌。
同步方法
您可以將 isBlockingSynchronousMethod = true
傳遞給原生方法,以將其標記為同步方法。
- Java
- Kotlin
@ReactMethod(isBlockingSynchronousMethod = true)
@ReactMethod(isBlockingSynchronousMethod = true)
目前,我們不建議這樣做,因為同步呼叫方法可能會帶來嚴重的效能損失,並將執行緒相關的錯誤引入您的原生模組。此外,請注意,如果您選擇啟用 isBlockingSynchronousMethod
,您的應用程式將無法再使用 Google Chrome 偵錯工具。這是因為同步方法需要 JS VM 與應用程式共用記憶體。對於 Google Chrome 偵錯工具,React Native 在 Google Chrome 中的 JS VM 內執行,並透過 WebSockets 與行動裝置非同步通訊。
註冊模組(Android 特定)
一旦編寫了原生模組,就需要向 React Native 註冊。為了做到這一點,您需要將您的原生模組新增到 ReactPackage
,並向 React Native 註冊 ReactPackage
。在初始化期間,React Native 將循環遍歷所有套件,並針對每個 ReactPackage
,註冊其中的每個原生模組。
React Native 調用 ReactPackage
上的方法 createNativeModules()
,以取得要註冊的原生模組清單。對於 Android,如果模組未在 createNativeModules 中實例化和傳回,則它將無法從 JavaScript 存取。
若要將您的原生模組新增到 ReactPackage
,首先建立一個名為 (MyAppPackage.java
或 MyAppPackage.kt
) 的新 Java/Kotlin 類別,該類別在 android/app/src/main/java/com/your-app-name/
資料夾內實作 ReactPackage
然後新增以下內容
- 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 作為替代方案。TurboReactPackage 實作了一個
getModule(String name, ReactApplicationContext rac)
方法,而不是傳回實例化的原生模組物件清單的createNativeModules
,該方法在需要時建立原生模組物件。TurboReactPackage 目前實作起來有點複雜。除了實作getModule()
方法之外,您還必須實作getReactModuleInfoProvider()
方法,該方法傳回套件可以實例化的所有原生模組的清單,以及一個實例化它們的函數,範例這裡。再次強調,使用 TurboReactPackage 將允許您的應用程式擁有更快的啟動時間,但目前編寫起來有點麻煩。因此,如果您選擇使用 TurboReactPackage,請謹慎行事。
若要註冊 CalendarModule
套件,您必須將 MyAppPackage
新增到 ReactNativeHost 的 getPackages()
方法中傳回的套件清單中。開啟您的 MainApplication.java
或 MainApplication.kt
檔案,該檔案可以在以下路徑中找到:android/app/src/main/java/com/your-app-name/
。
找到 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 bundler 可以監看 JavaScript 中的變更並為您即時重建,但它不會對原生程式碼執行此操作。因此,如果您想測試最新的原生變更,您需要使用上述命令進行重建。
重點回顧✨
您現在應該能夠在應用程式中調用原生模組上的 createCalendarEvent()
方法。在我們的範例中,這是透過按下 NewModuleButton
來發生的。您可以透過檢視您在 createCalendarEvent()
方法中設定的日誌來確認這一點。您可以按照這些步驟來檢視您應用程式中的 ADB 日誌。然後,您應該能夠搜尋您的 Log.d
訊息(在我們的範例中為「使用名稱:testName 和位置: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 程式碼都將是類型安全的。這樣做也將使您更容易在未來切換到類型安全的原生模組。以下是如何將類型安全新增到 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;
在您的其他 JavaScript 檔案中,您可以存取原生模組並像這樣調用其方法
import CalendarModule from './CalendarModule';
CalendarModule.createCalendarEvent('foo', 'bar');
這假設您匯入
CalendarModule
的位置與CalendarModule.js
位於相同的階層中。請根據需要更新相對匯入。
參數類型
當在 JavaScript 中調用原生模組方法時,React Native 會將參數從 JS 物件轉換為它們的 Java/Kotlin 物件對應物。因此,例如,如果您的 Java 原生模組方法接受 double,則在 JS 中您需要使用數字呼叫該方法。React Native 將為您處理轉換。以下是原生模組方法支援的參數類型及其對應的 JavaScript 等效項的清單。
Java | Kotlin | JavaScript |
---|---|---|
布林值 | 布林值 | ?boolean |
boolean | boolean | |
Double | Double | ?number |
double | number | |
字串 | 字串 | string |
回呼 | 回呼 | 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
}
}
匯出常數
原生模組可以透過實作可在 JS 中使用的原生方法 getConstants()
來匯出常數。下面您將實作 getConstants()
並傳回一個 Map,其中包含您可以在 JavaScript 中存取的 DEFAULT_EVENT_NAME
常數
- 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 而改變。使用 Turbomodules,
getConstants()
將成為一個常規原生模組方法,並且每次調用都會觸及原生端。
回呼
原生模組也支援一種獨特的參數:回呼。回呼用於將資料從 Java/Kotlin 傳遞到 JavaScript 以用於非同步方法。它們也可以用於從原生端非同步執行 JavaScript。
為了建立一個具有回呼的原生模組方法,首先匯入 Callback
介面,然後將類型為 Callback
的新參數新增到您的原生模組方法。回呼參數有一些細微差別,這些細微差別將很快隨著 TurboModules 而消除。首先,您的函數參數中只能有兩個回呼 - 一個 successCallback 和一個 failureCallback。此外,原生模組方法呼叫的最後一個參數(如果它是函數)被視為 successCallback,而原生模組方法呼叫的倒數第二個參數(如果它是函數)被視為 failure callback。
- 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 的任何資料。請注意,您只能將可序列化的資料從原生程式碼傳遞到 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)
}
}
與回呼類似,原生模組方法可以拒絕或解析 Promise(但不能同時執行兩者),並且最多可以執行一次。這表示您可以呼叫成功回呼或失敗回呼,但不能同時呼叫兩者,並且每個回呼最多只能調用一次。但是,原生模組可以儲存回呼並稍後調用它。
此方法的 JavaScript 對應物傳回 Promise。這表示您可以在 async 函數中使用 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
,ReactNative 將其設定為 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 上,所有原生模組非同步方法都在一個執行緒上執行。原生模組不應對它們被呼叫的執行緒做出任何假設,因為目前的分配可能會在未來發生變更。如果需要阻塞呼叫,則應將繁重的工作分派到內部管理的 Worker 執行緒,並從那裡分發任何回呼。