Help us understand the problem. What is going on with this article?

React Nativeハンズオン〜Native Modules を作る(Android版)〜

More than 1 year has passed since last update.

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 クラスを実装します。

android/app/src/main/java/com/sample_android_native_modules/toast/ToastModule.java
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);
    }
}

ReactContextBaseJavaModulegetName というメソッドが実装されている必要があります(overrideしましょう)。
getName で定義した文字列をもとに JavaScript からNative Modules を呼び出すことができます。
チュートリアルでは ToastExample という名前で実装します。

android/app/src/main/java/com/sample_android_native_modules/toast/ToastModule.java
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に同期して通信する必要のある事前定義された値をキーするには便利です。
チュートリアルでは表示時間の制御に使うため記載しています。

android/app/src/main/java/com/sample_android_native_modules/toast/ToastModule.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に渡すためには、コールバックまたはイベントを発行します。(今回はやりませんがドキュメントには記載されているので興味わいた方は試してみてください)

android/app/src/main/java/com/sample_android_native_modules/toast/ToastModule.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 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 を継承したクラスを作成し、パッケージリストに追加します。

android/app/src/main/java/com/sample_android_native_modules/toast/ToastPackage.java
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 メソッドで提供する必要があります。

android/app/src/main/java/com/sample_android_native_modules/MainApplication.java
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モジュールでラップするのが一般的です。

lib/Toast.js
import { NativeModules } from 'react-native';
export default NativeModules.ToastExample;

ボタン配置してクリックされたタイミングで表示する

App.js
/**
 * 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,
  },
});

ボタンを押してこんな感じになったら完成です!

02gazou.png

時間のある人は

実は Toasta はすでに実装されています。
ドキュメント
ソースコード

下記のメソッドを作成してみましょう!

  • showWithGravity
  • showWithGravityAndOffset

まとめ

実際に書いてみて思ったよりかんたんに Native Module が作れたと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした