Android 原生模組
原生模組和原生元件是我們用於舊式架構的穩定技術。當新架構穩定後,它們將在未來被棄用。新架構使用 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/
資料夾內建立 (CalendarModule.java
或 CalendarModule.kt
) Java/Kotlin 檔案(此資料夾對 Kotlin 和 Java 來說都是相同的)。此 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),這對於需要連接到活動生命週期方法的原生模組很有用。使用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 中連結後,您可以按照 這些步驟 查看應用程式的記錄。
同步方法
您可以將 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,請先在 android/app/src/main/java/com/your-app-name/ 資料夾中建立一個新的 Java/Kotlin 類別,命名為(MyAppPackage.java 或 MyAppPackage.kt),並在其中實作 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 會讓您的應用程式有更快的啟動時間,但目前撰寫起來有點繁瑣。因此,如果您選擇使用 TurboReactPackages,請小心進行。
若要註冊 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() {
@SuppressWarnings("UnnecessaryLocalVariable")
List<ReactPackage> packages = new PackageList(this).getPackages();
// below MyAppPackage is added to the list of packages returned
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:
// packages.add(new 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 呼叫其原生方法。您可以繼續閱讀,以瞭解更多關於原生模組方法可用的引數類型,以及如何設定回呼和承諾等事項。
超越日曆原生模組
更好的原生模組匯出
如上所述,透過從 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 |
---|---|---|
布林值 | 布林值 | ?布林值 |
布林值 | 布林值 | |
雙精度 | 雙精度 | ?number |
double | number | |
字串 | 字串 | 字串 |
回呼函式 | 回呼函式 | 函式 |
承諾 | 承諾 | 承諾 |
可讀取地圖 | 可讀取地圖 | 物件 |
可讀取陣列 | 可讀取陣列 | 陣列 |
目前支援下列類型,但 TurboModules 不會支援。請避免使用這些類型
- 整數 Java/Kotlin -> ?number
- 浮點數 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()
來匯出常數,此方法在 JS 中可用。以下是實作 getConstants()
的方式,並傳回包含 DEFAULT_EVENT_NAME
常數的地圖,您可以在 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 將會變更此行為。在 TurboModules 中,
getConstants()
將會變成常規原生模組方法,每次呼叫都會觸發原生端。
回呼函式
原生模組也支援一種獨特的引數類型:回呼函式。回呼函式用於將資料從 Java/Kotlin 傳遞至 JavaScript,以供非同步方法使用。也可以用於從原生端非同步執行 JavaScript。
若要建立帶有回呼函式的原生模組方法,請先匯入 Callback
介面,然後為您的原生模組方法新增一個 Callback
類型的參數。回呼函式引數有一些細微差別,TurboModules 將會很快解除這些限制。首先,函式引數中只能包含兩個回呼函式:一個成功回呼函式和一個失敗回呼函式。此外,原生模組方法呼叫的最後一個引數(如果它是函式)會被視為成功回呼函式,而原生模組方法呼叫的倒數第二個引數(如果它是函式)會被視為失敗回呼函式。
- 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`);
},
);
};
承諾
原生模組也可以履行 承諾,這可以簡化您的 JavaScript,特別是在使用 ES2016 的 async/await 語法時。當原生模組 Java/Kotlin 方法的最後一個參數是承諾時,其對應的 JS 方法將傳回 JS 承諾物件。
將上述程式碼重新整理為使用承諾而非回呼,如下所示
- 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)
}
}
與回呼類似,原生模組方法可以拒絕或解決承諾(但不能同時進行),而且最多只能執行一次。這表示你可以呼叫成功回呼或失敗回呼,但不能同時進行,而且每個回呼都只能呼叫一次。不過,原生模組可以儲存回呼並稍後呼叫它。
此方法的 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
,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)
承諾遭到拒絕時,React Native App 中的錯誤訊息

傳送事件至 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 取得活動結果
如果您想從使用 startActivityForResult
啟動的活動取得結果,您需要監聽 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
}
讓我們實作一個基本的影像選擇器來示範這一點。影像選擇器會將 pickImage
方法公開給 JavaScript,在呼叫時會傳回影像路徑。
- 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"
}
}
監聽生命週期事件
監聽活動的生命週期事件(例如 onResume
、onPause
等)的方式與實作 ActivityEventListener
的方式非常類似。模組必須實作 LifecycleEventListener
。然後,您需要在模組的建構函式中註冊監聽程式,如下所示
- Java
- Kotlin
reactContext.addLifecycleEventListener(this);
reactContext.addLifecycleEventListener(this)
現在,您可以透過實作下列方法來監聽活動的生命週期事件
- 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 上,所有原生模組非同步方法都在單一執行緒上執行。原生模組不應對其被呼叫的執行緒有任何假設,因為目前的指派可能會在未來變更。如果需要封鎖呼叫,應將繁重的工作分派到內部管理的工作執行緒,並從那裡分派任何回呼。