メインコンテンツにスキップ

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 プロジェクトはここにあります。

Image of opening up an Android project within a React Native app inside of Android Studio.
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 クラスが含まれます。

Image of adding a class called CalendarModule.java within the Android Studio.
CalendarModuleClass を追加する方法の画像

次に、次のコンテンツを追加します

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

ご覧のとおり、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" を返します。

// add to CalendarModule.java
@Override
public String getName() {
return "CalendarModule";
}

ネイティブモジュールは、JS で次のようにアクセスできます

const {CalendarModule} = ReactNative.NativeModules;

JavaScript へのネイティブメソッドのエクスポート

次に、JavaScript で呼び出すことができ、カレンダーイベントを作成するネイティブモジュールにメソッドを追加する必要があります。 JavaScript から呼び出すネイティブモジュールメソッドはすべて、@ReactMethod で注釈を付ける必要があります。

CalendarModule のメソッド createCalendarEvent() を設定して、JS で CalendarModule.createCalendarEvent() を通じて呼び出すことができるようにします。 現時点では、メソッドは名前と場所を文字列として受け取ります。 引数の型のオプションについては、後で説明します。

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

メソッドにデバッグログを追加して、アプリケーションから呼び出したときに呼び出されたことを確認します。 以下は、Android util パッケージから Log クラスをインポートして使用する方法の例です。

import android.util.Log;

@ReactMethod
public void createCalendarEvent(String name, String location) {
Log.d("CalendarModule", "Create event called with name: " + name
+ " and location: " + location);
}

ネイティブモジュールの実装と JavaScript でのフックが完了したら、これらの手順に従って、アプリからログを表示できます。

同期メソッド

isBlockingSynchronousMethod = true をネイティブメソッドに渡して、同期メソッドとしてマークできます。

@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 クラスを作成します

次に、次のコンテンツを追加します

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

}

このファイルは、作成したネイティブモジュール 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()が返すパッケージリストに自分のパッケージを追加してください。

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

これで、Android用のネイティブモジュールが正常に登録されました!

ビルドしたもののテスト

この時点で、Androidのネイティブモジュールの基本的な足場をセットアップしました。ネイティブモジュールにアクセスし、JavaScriptでエクスポートされたメソッドを呼び出してテストしてください。

アプリケーション内で、ネイティブモジュールのcreateCalendarEvent()メソッドの呼び出しを追加したい場所を見つけてください。以下は、アプリに追加できるコンポーネントNewModuleButtonの例です。NewModuleButtononPress()関数内でネイティブモジュールを呼び出すことができます。

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()を呼び出すことができます。以下に、NewModuleButtononPress()メソッドに追加した例を示します。

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

最後のステップは、React Nativeアプリをリビルドして、最新のネイティブコード(新しいネイティブモジュールを含む)を利用できるようにすることです。React Nativeアプリケーションがあるコマンドラインで、以下を実行してください。

npm run android

反復的なビルド

これらのガイドを進めてネイティブモジュールを反復する場合、JavaScriptから最新の変更にアクセスするには、アプリケーションのネイティブリビルドを実行する必要があります。これは、記述しているコードがアプリケーションのネイティブ部分に存在するためです。React NativeのmetroバンドラーはJavaScriptの変更を監視し、その場でリビルドできますが、ネイティブコードではそうではありません。したがって、最新のネイティブ変更をテストする場合は、上記のコマンドを使用してリビルドする必要があります。

まとめ✨

これで、アプリのネイティブモジュールでcreateCalendarEvent()メソッドを呼び出せるはずです。この例では、NewModuleButtonを押すことで実行されます。createCalendarEvent()メソッドで設定したログを表示することで、これを確認できます。これらの手順に従って、アプリでADBログを表示できます。次に、Log.dメッセージ(この例では「Create event called with name: testName and location: testLocation」)を検索し、ネイティブモジュールメソッドを呼び出すたびにメッセージが記録されるのを確認できるはずです。

Image of logs.
Android StudioのADBログの画像

この時点で、Androidネイティブモジュールを作成し、React NativeアプリケーションのJavaScriptからそのネイティブメソッドを呼び出しました。続けて、ネイティブモジュールメソッドで使用できる引数型や、コールバックとプロミスの設定方法などについて詳しく学ぶことができます。

カレンダーネイティブモジュールを超えて

ネイティブモジュールのより良いエクスポート

上記のように、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同等の型のリストです。

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

以下の型は現在サポートされていますが、TurboModulesではサポートされません。使用は避けてください

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

上記にリストされていない引数型の場合、自分で変換を処理する必要があります。たとえば、Androidでは、Date変換はすぐにサポートされていません。ネイティブメソッド内で、Date型への変換を自分で処理できます。

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

定数のエクスポート

ネイティブモジュールは、JSで使用可能なネイティブメソッドgetConstants()を実装することで定数をエクスポートできます。以下では、getConstants()を実装し、JavaScriptでアクセスできるDEFAULT_EVENT_NAME定数を含むMapを返します。

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

定数は、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番目の引数が関数の場合、失敗コールバックとして扱われます。

import com.facebook.react.bridge.Callback;

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

Java/Kotlinメソッドでコールバックを呼び出し、JavaScriptに渡したいデータを渡すことができます。ネイティブコードからJavaScriptにシリアライズ可能なデータのみを渡せることに注意してください。ネイティブオブジェクトを渡す必要がある場合はWriteableMapsを使用し、コレクションを使用する必要がある場合はWritableArraysを使用できます。コールバックは、ネイティブ関数が完了した直後に呼び出されないことも重要です。以下に、以前の呼び出しで作成されたイベントのIDがコールバックに渡されます。

  @ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
Integer 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の規則に従い、コールバックに渡される最初の引数をエラーオブジェクトとして扱うことです。

  @ReactMethod
public void createCalendarEvent(String name, String location, Callback callBack) {
Integer 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のコールバックを使用することです。

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

次に、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プロミスオブジェクトを返します。

上記のコードをコールバックではなくプロミスを使用するようにリファクタリングすると、次のようになります。

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

コールバックと同様に、ネイティブモジュールメソッドはプロミスを拒否または解決できます(両方はできません)。また、最大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);
}
};

拒否メソッドは、次の引数のさまざまな組み合わせを受け入れます。

String code, String message, WritableMap userInfo, Throwable throwable

詳細については、Promise.javaインターフェースをこちらで確認できます。userInfoが提供されない場合、ReactNativeはそれをnullに設定します。残りのパラメータについては、React Nativeはデフォルト値を使用します。message引数は、エラーコールスタックの最上部に表示されるエラーmessageを提供します。以下は、Java/Kotlinでの以下のreject呼び出しから、JavaScriptで表示されるエラーメッセージの例です。

Java/Kotlinのreject呼び出し

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

PromiseがrejectされたときにReact Nativeアプリに表示されるエラーメッセージ

Image of error message in React Native app.
エラーメッセージの画像

JavaScriptへのイベント送信

ネイティブモジュールは、直接呼び出されなくてもJavaScriptにイベントを通知できます。たとえば、ネイティブのAndroidカレンダーアプリのカレンダーイベントがまもなく発生することをJavaScriptに通知したい場合があります。これを行う最も簡単な方法は、以下のコードスニペットのようにReactContextから取得できるRCTDeviceEventEmitterを使用することです。

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

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の変更に対する耐性が高いため、前者が推奨されます。次に、モジュールのコンストラクタで次のようにリスナーを登録する必要があります。

reactContext.addActivityEventListener(mActivityResultListener);

これで、次のメソッドを実装することでonActivityResultをリッスンできます。

@Override
public void onActivityResult(
final Activity activity,
final int requestCode,
final int resultCode,
final Intent intent) {
// Your logic here
}

これを実証するために、基本的な画像ピッカーを実装してみましょう。画像ピッカーは、JavaScriptにpickImageメソッドを公開し、呼び出されると画像のパスを返します。

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

ライフサイクルイベントのリスニング

onResumeonPauseなどのアクティビティのライフサイクルイベントのリスニングは、ActivityEventListenerの実装方法と非常によく似ています。モジュールはLifecycleEventListenerを実装する必要があります。次に、モジュールのコンストラクタで次のようにリスナーを登録する必要があります。

reactContext.addLifecycleEventListener(this);

これで、次のメソッドを実装することで、アクティビティのライフサイクルイベントをリッスンできます。

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

スレッド

現在、Androidでは、すべてのネイティブモジュール非同期メソッドは1つのスレッドで実行されます。ネイティブモジュールは、どのスレッドで呼び出されているかについて何も想定すべきではありません。現在の割り当ては、将来変更される可能性があります。ブロッキング呼び出しが必要な場合は、負荷の高い作業を内部で管理されたワーカースレッドにディスパッチし、そこからコールバックを配布する必要があります。