Native の API を利用したいが、ReactNative がサポートしていない場合自前でモジュールを作る必要があります。
やり方は公式のドキュメントで紹介されているので、流れに沿ってハンズオンを行います。
Macの方をメインターゲットにしているので、Windowsの方は適宜読み替えてください。
成果物はここにあります。
https://github.com/OshiroSeiya/sample_android_native_modules
環境構築
公式のドキュメントを参考に入れていきます。
注意 :Androidの開発環境構築はミスし易いので手順をしっかり確認してください
yarn は僕が使いたいので入れます。
$ brew install node
$ brew install yarn
$ brew install watchman
$ npm install -g react-native-cli
Java Development Kitの導入
AndroidStudio の導入
インストールタイプを選択するように求められたら、「Custom」セットアップを選択します。
次のすべてのチェックボックスがオンになっていることを確認します。
- Android SDK
- Android SDK Platform
- Performance (Intel ® HAXM)
- Android Virtual Device
「次へ」をクリックしてこれらのコンポーネントをすべてインストールします。
セットアップが完了し、ウェルカム画面が表示されたら、次の手順に進みます。
Android SDK の導入
Android Studioはデフォルトで最新のAndroid SDKをインストールします。
リアクションの構築ネイティブコードを使ったネイティブアプリは、特にAndroid 6.0(Marshmallow)SDKが必要です。
追加のAndroid SDKは、Android StudioのSDKマネージャからインストールできます。
SDKマネージャには、「Welcome to Android Studio」画面からアクセスできます。
「Configure」をクリックし、「SDK Manager」を選択します。
「Show Package Details」にチェックを入れて詳細を見れるようにします。
次のすべてのチェックボックスがオンになっていることを確認します。
Android 6.0 (Marshmallow)
- Google APIs
- Android SDK Platform 23
- Intel x86 Atom_64 System Image
- Google APIs Intel x86 Atom_64 System Image
次に「SDK Tools」タブを選択して「Android SDK Build-Tools」の 23.0.1
を選択します。
問題なければ「Apply」を選択してインストールします。
環境変数の設定
.bash_profile
などに下記を追加します。
export ANDROID_HOME=$HOME/Library/Android/sdk
export PATH=$PATH:$ANDROID_HOME/tools
export PATH=$PATH:$ANDROID_HOME/platform-tools
編集が終わったら設定をロードします。
source $HOME/.bash_profile
プロジェクトの作成
$ react-native init sample_android_native_modules
Androidエミュレータの準備
React Native の Androidアプリを実行するには、Androidデバイスが必要です。
これは物理的なAndroidデバイスでも、エミュレータでもいいですが、やりやすいようにエミュレータを利用することにします。
まず、Android Studio でプロジェクトを開きます。
プロジェクトは ./sample_android_native_modules/android
を指定してください。
起動後に Gradle をアップデートするか確認されるかもしれないですが、今回はアップデートなしを選択してください。
その後 「Tools」をクリックし「Android」を選択「AVD Manager」を選択します。
「Create Virtual Device...」をクリックして新しい仮想デバイスを作成します。
「phone」から好きなデバイスを選んで「Next」を選択します。
「x86 Images」タブを選択して「Marshmallow 23 x86_64 Android 6.0 (Google APIs)」を選択します。
「Next」を選択後「Finish」を選択します。
緑色の再生マークをクリックしてエミュレータを起動します。
React Native プロジェクトのディレクトリに移動してアプリを実行します。
$ cd sample_android_native_modules
$ react-native run-android
エミュレータに下記のような画面が表示されれば成功です。
Native Modules チュートリアル開始
Toast を実装して行きます。JavaScriptからトーストメッセージを作成できるようにします。
モジュールの作成する
まず、Native Modules は ReactContextBaseJavaModule
を拡張することで JavaScript から呼び出せる機能を実装できるので ToastModule
クラスを実装します。
package com.sample_android_native_modules.toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
public class ToastModule extends ReactContextBaseJavaModule {
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
}
ReactContextBaseJavaModule
は getName
というメソッドが実装されている必要があります(overrideしましょう)。
getName
で定義した文字列をもとに JavaScript からNative Modules を呼び出すことができます。
チュートリアルでは ToastExample
という名前で実装します。
package com.sample_android_native_modules.toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
public class ToastModule extends ReactContextBaseJavaModule {
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
+ @Override
+ public String getName() {
+ return "ToastExample";
+ }
}
getConstants
はオプションです。
機能は、JavaScriptに公開されている定数値を返します。
必須ではないですが、JavaScriptからJavaに同期して通信する必要のある事前定義された値をキーするには便利です。
チュートリアルでは表示時間の制御に使うため記載しています。
package com.sample_android_native_modules.toast;
+ import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
+ import java.util.Map;
+ import java.util.HashMap;
public class ToastModule extends ReactContextBaseJavaModule {
+ private static final String DURATION_SHORT_KEY = "SHORT";
+ private static final String DURATION_LONG_KEY = "LONG";
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "ToastExample";
}
+ @Override
+ public Map<String, Object> getConstants() {
+ final Map<String, Object> constants = new HashMap<>();
+ constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
+ constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
+ return constants;
+ }
}
実装したメソッドを JavaScript に公開するには、 Java メソッドに @ReactMethod
を使用して注釈を付ける必要があります。 戻り型は常に無効です。
React Native のブリッジは非同期であるため、結果をJavaScriptに渡すためには、コールバックまたはイベントを発行します。(今回はやりませんがドキュメントには記載されているので興味わいた方は試してみてください)
package com.sample_android_native_modules.toast;
import android.widget.Toast;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
+ import com.facebook.react.bridge.ReactMethod;
import java.util.Map;
import java.util.HashMap;
public class ToastModule extends ReactContextBaseJavaModule {
private static final String DURATION_SHORT_KEY = "SHORT";
private static final String DURATION_LONG_KEY = "LONG";
public ToastModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "ToastExample";
}
@Override
public Map<String, Object> getConstants() {
final Map<String, Object> constants = new HashMap<>();
constants.put(DURATION_SHORT_KEY, Toast.LENGTH_SHORT);
constants.put(DURATION_LONG_KEY, Toast.LENGTH_LONG);
return constants;
}
+ @ReactMethod
+ public void show(String message, int duration) {
+ Toast.makeText(getReactApplicationContext(), message, duration).show();
+ }
}
引数の型は下記のように対応します。
Java | JavaScript |
---|---|
Boolean | Bool |
Integer | Number |
Double | Number |
Float | Number |
String | String |
Callback | function |
ReadableMap | Object |
ReadableArray | Array |
ReadableMap と ReadableArray は React Native 独自のものなので github を参照してください
ReadableMap
ReadableArray
モジュールの登録する
JavaScript から呼び出すためにはモジュールをアプリケーションに登録する必要があります。
ReactPackage
を継承したクラスを作成し、パッケージリストに追加します。
package com.sample_android_native_modules.toast;
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 ToastPackage 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 ToastModule(reactContext));
return modules;
}
}
パッケージは、MainApplication.java
ファイルの getPackages
メソッドで提供する必要があります。
package com.sample_android_native_modules;
import android.app.Application;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;
+ import com.sample_android_native_modules.toast.ToastPackage;
import java.util.Arrays;
import java.util.List;
public class MainApplication extends Application implements ReactApplication {
private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
@Override
public boolean getUseDeveloperSupport() {
return BuildConfig.DEBUG;
}
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
- new MainReactPackage()
+ new MainReactPackage(),
+ new ToastPackage()
);
}
@Override
protected String getJSMainModuleName() {
return "index";
}
};
@Override
public ReactNativeHost getReactNativeHost() {
return mReactNativeHost;
}
@Override
public void onCreate() {
super.onCreate();
SoLoader.init(this, /* native exopackage */ false);
}
}
JavaScript からの呼び出しし易いようにラッパー用意する
JavaScriptから新しい機能に簡単にアクセスできるようにするには、ネイティブモジュールをJavaScriptモジュールでラップするのが一般的です。
import { NativeModules } from 'react-native';
export default NativeModules.ToastExample;
ボタン配置してクリックされたタイミングで表示する
/**
* Sample React Native App
* https://github.com/facebook/react-native
* @flow
*/
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
+ Button,
View
} from 'react-native';
+ import Toast from './lib/Toast';
const instructions = Platform.select({
ios: 'Press Cmd+R to reload,\n' +
'Cmd+D or shake for dev menu',
android: 'Double tap R on your keyboard to reload,\n' +
'Shake or press menu button for dev menu',
});
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<View style={styles.container}>
<Text style={styles.welcome}>
Welcome to React Native!
</Text>
<Text style={styles.instructions}>
To get started, edit App.js
</Text>
<Text style={styles.instructions}>
{instructions}
</Text>
+ <Button
+ onPress={() => Toast.show('表示できた!', Toast.SHORT)}
+ title="Show Toast"
+ accessibilityLabel="Show toast"
+ />
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
});
ボタンを押してこんな感じになったら完成です!
時間のある人は
実は Toasta はすでに実装されています。
ドキュメント
ソースコード
下記のメソッドを作成してみましょう!
- showWithGravity
- showWithGravityAndOffset
まとめ
実際に書いてみて思ったよりかんたんに Native Module が作れたと思います。