LoginSignup
6
4

More than 5 years have passed since last update.

React Native ブリッジ書き方 ~ Android編 ~

Posted at

React Nativeでネイティブの機能を使いたい!って時はありませんか?
しかし、GitHubからお目当てのサードパーティーがない。バグだらけでメンテされてない。など、どうしてもネイティブ側も書かないといけない。みたいな状況が発生します。

そんな時に役立つ、ネイティブの機能をReact(js)側に橋渡しする方法を共有したいと思います。

TL;DR

AndroidのNative機能を使う際、音声やバックグラウンド処理などのViewがない機能とカメラのプレビューのようなViewありきの機能がある。

ViewがあるかないかでReactに値を渡すメソッドが違うので、ここでは分けて考える。

共通部分

ReactPackageインタフェースをimplementsしたクラスを実装する

  1. view以外の機能を提供するNativeModuleを、React側で使用するために登録する
  2. Nativeが提供するUI ComponentをReact側で使用するためにviewManagerを登録する
  3. createJSModulesは現状必須でない
RNCameraPackage.java
package ...

import ...

public class RNCameraPackage implements ReactPackage {

    @Override
    public List<NativeModule> createNativeModules(ReactApplicationContext reactApplicationContext) {
        return Arrays.<NativeModule>asList(
                new RCTCameraModule(reactApplicationContext),
                new CameraModule(reactApplicationContext),
                new FaceDetectorModule(reactApplicationContext)
        );
    }

    // Deprecated in RN 0.47
    public List<Class<? extends JavaScriptModule>> createJSModules() {
        return Collections.emptyList();
    }

    @Override
    public List<ViewManager> createViewManagers(ReactApplicationContext reactApplicationContext) {
        return Arrays.<ViewManager>asList(
                new RCTCameraViewManager(),
                new CameraViewManager()
        );
    }
}

UI以外のNative機能をブリッジしたい場合

ReactContextBaseJavaModuleを継承したモジュールを実装
  1. android/app/src側のjavaファイルにて、ReactContextBaseJavaModuleクラスを継承したクラスをつくる。

  2. Module名を書く

  3. React側で使う定数を定義する(なければなしで良い)

  4. React側で使うメソッドを定義する。この際、@ReactMethodアノテーションを付与する。

CameraModule.java
//① 
public class CameraModule extends ReactContextBaseJavaModule {
    private static final String TAG = "CameraModule";

    ...

    public CameraModule(ReactApplicationContext reactContext) {
        super(reactContext);
        mScopedContext = new ScopedContext(reactContext);
    }

    ...

    //②
    @Override
    public String getName() {
        return "RNCameraModule";
    }

    //③
    @Override
    public Map<String, Object> getConstants() {
        return Collections.unmodifiableMap(new HashMap<String, Object>() {
            {
                put("Type", getTypeConstants());
                put("FlashMode", getFlashModeConstants());
                ...
            }
        });
    }

    ...

    //④
    @ReactMethod
    public void takePicture(final ReadableMap options, final int viewTag, final Promise promise) {
        final ReactApplicationContext context = getReactApplicationContext();
        final File cacheDirectory = mScopedContext.getCacheDirectory();
        UIManagerModule uiManager = context.getNativeModule(UIManagerModule.class);
        uiManager.addUIBlock(new UIBlock() {
        @Override
        public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) {
            RNCameraView cameraView = (RNCameraView) nativeViewHierarchyManager.resolveView(viewTag);
                try {
                    if (cameraView.isCameraOpened()) {
                        cameraView.takePicture(options, promise, cacheDirectory);
                    } else {
                        promise.reject("E_CAMERA_UNAVAILABLE", "Camera is not running");
                    }
                } catch (Exception e) {
                  promise.reject("E_CAMERA_BAD_VIEWTAG", "takePictureAsync: Expected a Camera component");
                }
            }
        });
    }

    ...

}
ReactMethodを書く際に、JSからの引数が存在し、型がObjectやArrayの場合
Object => ReadableMap
Array => ReadableArray

で型の置き換えを行い、引数を使う。
また、ReactMethodで扱う引数はすべてを使う。

UI系のNative機能をブリッジしたい場合

  1. com.facebook.react.uimanager.ViewGroupManagerをimport
  2. ViewGroupManagerを継承したクラスを作成
  3. getName()をOverrideしてModule名を決める
  4. createViewInstanceOverrideして、自分が使いたいUI Componentを記述して、そのコンポーネントを返すメソッドを作成
CameraViewManager.java
package com.example;

//①
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.common.MapBuilder;
import com.facebook.react.uimanager.ThemedReactContext;
import com.facebook.react.uimanager.ViewGroupManager;

//②
public class CameraViewManager extends ViewGroupManager<RNCameraView> {
    private static final String REACT_CLASS = "RNCamera";

    @Override
    public void onDropViewInstance(RNCameraView view) {
        view.stop();
        super.onDropViewInstance(view);
    }

    //③
    // Component name that will be called from JavaScript
    @Override
    public String getName() {
        return REACT_CLASS;
    }

    //④
    // Return the view component instantiated with Activity context
    @Override
    public TextView createViewInstance(ThemedReactContext reactContext) {
        return new RNCameraView(themedReactContext);
    }
}

JS側でNativeModuleを使う

  1. NativeModule.{getName()が返す値}を呼び出す。(ここでは別ファイルにして使えるようにRNCamera.jsでexport.
OriginalCamera.js
import { NativeModules } from `react-native`;

export default NativeModules.RNCameraModule;
6
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
4