Androidネイティブモジュール
ネイティブモジュールとネイティブコンポーネントは、レガシーアーキテクチャで使用される安定したテクノロジーです。 新しいアーキテクチャが安定すると、将来的に非推奨になる予定です。 新しいアーキテクチャでは、Turbo ネイティブモジュールとFabric ネイティブコンポーネントを使用して、同様の結果を実現します。
Android 用ネイティブモジュールへようこそ。 まず、ネイティブモジュールとは何かについての入門として、ネイティブモジュール入門をお読みください。
カレンダーネイティブモジュールの作成
次のガイドでは、JavaScript から Android のカレンダー API にアクセスできるネイティブモジュール CalendarModule
を作成します。 最後に、JavaScript から CalendarModule.createCalendarEvent('Dinner Party', 'My House');
を呼び出し、カレンダーイベントを作成する Java/Kotlin メソッドを呼び出せるようになります。
設定
開始するには、Android Studio で React Native アプリケーション内の Android プロジェクトを開きます。React Native アプリ内の Android プロジェクトはここにあります。

ネイティブコードを記述するには、Android Studio を使用することをお勧めします。 Android Studio は Android 開発用に構築された IDE であり、これを使用すると、コード構文エラーのような些細な問題を迅速に解決できます。
また、Java/Kotlin コードを反復処理する際にビルドを高速化するために、Gradle Daemon を有効にすることをお勧めします。
カスタムネイティブモジュールファイルの作成
最初のステップは、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) へのアクセスを提供します。 また、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()
を設定して、JS で CalendarModule.createCalendarEvent()
を通じて呼び出すことができるようにします。 現時点では、メソッドは名前と場所を文字列として受け取ります。 引数の型のオプションについては、後で説明します。
- 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 内で実行され、WebSocket を介してモバイルデバイスと非同期的に通信します。
モジュールの登録 (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
を実装する (MyAppPackage.java
または MyAppPackage.kt
) という名前の新しい Java/Kotlin クラスを作成します
次に、次のコンテンツを追加します
- 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
パッケージを登録するには、ReactNativeHostのgetPackages()
メソッドで返されるパッケージリストにMyAppPackage
を追加する必要があります。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バンドラーはJavaScriptの変更を監視し、その場でリビルドできますが、ネイティブコードではそうではありません。したがって、最新のネイティブ変更をテストする場合は、上記のコマンドを使用してリビルドする必要があります。
まとめ✨
これで、アプリのネイティブモジュールでcreateCalendarEvent()
メソッドを呼び出せるはずです。この例では、NewModuleButton
を押すことで実行されます。createCalendarEvent()
メソッドで設定したログを表示することで、これを確認できます。これらの手順に従って、アプリでADBログを表示できます。次に、Log.d
メッセージ(この例では「Create event called with name: testName and location: 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 Native Moduleメソッドがdoubleを受け入れる場合、JSでは数値でメソッドを呼び出す必要があります。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
}
}
定数のエクスポート
ネイティブモジュールは、JSで使用可能なネイティブメソッドgetConstants()
を実装することで定数をエクスポートできます。以下では、getConstants()
を実装し、JavaScriptでアクセスできるDEFAULT_EVENT_NAME
定数を含むMapを返します。
- 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でまもなく解消されるいくつかのニュアンスがあります。まず、関数引数に成功コールバックと失敗コールバックの2つのコールバックのみを含めることができます。さらに、ネイティブモジュールメソッド呼び出しの最後の引数が関数の場合、成功コールバックとして扱われ、ネイティブモジュールメソッド呼び出しの最後から2番目の引数が関数の場合、失敗コールバックとして扱われます。
- 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}`);
},
);
};
もう1つ注意すべき重要な詳細は、ネイティブモジュールメソッドが1つのコールバックを1回のみ呼び出すことができるということです。つまり、成功コールバックまたは失敗コールバックのいずれかを呼び出すことができますが、両方を呼び出すことはできず、各コールバックは最大1回しか呼び出すことができません。ただし、ネイティブモジュールはコールバックを保存して後で呼び出すことができます。
コールバックを使用するエラー処理には2つのアプローチがあります。1つ目は、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`);
},
);
};
プロミス
ネイティブモジュールは、プロミスを満たすこともでき、特にES2016のasync/await構文を使用する場合にJavaScriptを簡素化できます。ネイティブモジュール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)
}
}
コールバックと同様に、ネイティブモジュールメソッドはプロミスを拒否または解決できます(両方はできません)。また、最大1回しか実行できません。つまり、成功コールバックまたは失敗コールバックのいずれかを呼び出すことができますが、両方を呼び出すことはできず、各コールバックは最大1回しか呼び出すことができません。ただし、ネイティブモジュールはコールバックを保存して後で呼び出すことができます。
このメソッドのJavaScriptの対応物は、プロミスを返します。これは、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);
}
};
拒否メソッドは、次の引数のさまざまな組み合わせを受け入れます。
- 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がrejectされたときにReact Nativeアプリに表示されるエラーメッセージ

JavaScriptへのイベント送信
ネイティブモジュールは、直接呼び出されなくてもJavaScriptにイベントを通知できます。たとえば、ネイティブのAndroidカレンダーアプリのカレンダーイベントがまもなく発生することをJavaScriptに通知したい場合があります。これを行う最も簡単な方法は、以下のコードスニペットのようにReactContext
から取得できるRCTDeviceEventEmitter
を使用することです。
- 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からのActivityResultの取得
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
}
これを実証するために、基本的な画像ピッカーを実装してみましょう。画像ピッカーは、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"
}
}
ライフサイクルイベントのリスニング
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では、すべてのネイティブモジュール非同期メソッドは1つのスレッドで実行されます。ネイティブモジュールは、どのスレッドで呼び出されているかについて何も想定すべきではありません。現在の割り当ては、将来変更される可能性があります。ブロッキング呼び出しが必要な場合は、負荷の高い作業を内部で管理されたワーカースレッドにディスパッチし、そこからコールバックを配布する必要があります。