ReactNative呼叫原生模組

code_xzh發表於2017-01-08

概述

有時候App需要訪問平臺API,但React Native可能還沒有相應的模組包裝;或者你需要複用一些Java程式碼,而不是用Javascript重新實現一遍;又或者你需要實現某些高效能的、多執行緒的程式碼,譬如圖片處理、資料庫、或者各種高階擴充套件等等。
我們知道React Native本身對這種偏業務和底層呼叫是不關心的,這時候我們就想到了原生元件,我們通過呼叫原生元件,然後經過特定的封裝來達到效果。如我們在原生開發中常見的Toast為例:

原生模組封裝

假設我們希望可以從Javascript發起一個Toast訊息,Android會顯示在螢幕的下方,會停留一段時間。我們來看一下官方給出的例子。

建立一個繼承了ReactContextBaseJavaModule的Java類,它可以實現一些JavaScript所需的功能。我們這裡的目標是可以在JavaScript裡寫ToastAndroid.show(`Awesome`, ToastAndroid.SHORT);,來調起一個Toast通知。

package com.facebook.react.modules.toast;

import android.widget.Toast;

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;

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

ReactContextBaseJavaModule要求派生類實現getName方法,這個名字返回的字串可以自取,但是不能命名為`ToastAndroid`,因為RN已經內建了一個名為ToastAndroid的模組,執行時會報錯名字衝突!

  @Override
  public String getName() {
    return "ToastAndroid";
  }

注:模組名前的RCT字首會被自動移除。所以如果返回的字串為”RCTToastAndroid”,在JavaScript端依然通過React.NativeModules.ToastAndroid訪問到這個模組。
接下來我們需要設定一個可選的方法getContants(),用於彈出時間選擇(及Toast.Length)

 @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使用。

 @ReactMethod
  public void show(String message, int duration) {
    Toast.makeText(getReactApplicationContext(), message, duration).show();
  }

在Java這邊要做的最後一件事就是註冊這個模組。我們需要在應用的Package類的createNativeModules方法中新增這個模組。如果模組沒有被註冊,它也無法在JavaScript中被訪問到。

class AnExampleReactPackage implements ReactPackage {

  @Override
  public List<Class<? extends JavaScriptModule>> createJSModules() {
    return Collections.emptyList();
  }

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

這個package需要在MainApplication.java檔案的getPackages方法中提供。這個檔案位於你的react-native應用資料夾的android目錄中。具體路徑是: android/app/src/main/java/com/your-app-name/MainApplication.java.

protected List<ReactPackage> getPackages() {
    return Arrays.<ReactPackage>asList(
            new MainReactPackage(),
            new AnExampleReactPackage()); // <-- 新增這一行,類名替換成你的Package類的名字.
}

那麼在React Native中怎麼使用呢?為了讓你的功能從JavaScript端訪問起來更為方便,通常我們都會把原生模組封裝成一個JavaScript模組。

`use strict`;

import { NativeModules } from `react-native`;

export default NativeModules.ToastAndroid;

最後呼叫javascript模組就好了。

import ToastAndroid from `./ToastAndroid`;
ToastAndroid.show(`Awesome`, ToastAndroid.SHORT);

未完待續..


相關文章