React Native調(diào)用原生模塊

本篇主要參考中文網(wǎng)鏈接 ,不同的是,把我自己當(dāng)時(shí)接入感到迷惑的地方,詳細(xì)說(shuō)一下。

有時(shí)候App需要訪問(wèn)平臺(tái)API,但React Native可能還沒(méi)有相應(yīng)的模塊包裝;或者你需要復(fù)用一些Java代碼,而不是用Javascript重新實(shí)現(xiàn)一遍;又或者你需要實(shí)現(xiàn)某些高性能的、多線程的代碼,譬如圖片處理、數(shù)據(jù)庫(kù)、或者各種高級(jí)擴(kuò)展等等。

我們把React Native設(shè)計(jì)為可以在其基礎(chǔ)上編寫(xiě)真正的原生代碼,并且可以訪問(wèn)平臺(tái)所有的能力。這是一個(gè)相對(duì)高級(jí)的特性,我們并不認(rèn)為它應(yīng)當(dāng)在日常開(kāi)發(fā)的過(guò)程中經(jīng)常出現(xiàn),但具備這樣的能力是很重要的。如果React Native還不支持某個(gè)你需要的原生特性,你應(yīng)當(dāng)可以自己實(shí)現(xiàn)該特性的封裝。

Toast模塊

本向?qū)?huì)用Toast作為例子。假設(shè)我們希望可以從Javascript發(fā)起一個(gè)Toast消息(Android中的一種會(huì)在屏幕下方彈出、保持一段時(shí)間的消息通知)
我們首先來(lái)創(chuàng)建一個(gè)原生模塊。一個(gè)原生模塊是一個(gè)繼承了ReactContextBaseJavaModule的Java類(lèi),它可以實(shí)現(xiàn)一些JavaScript所需的功能。我們這里的目標(biāo)是可以在JavaScript里寫(xiě)ToastAndroid.show('Awesome', ToastAndroid.SHORT);,來(lái)調(diào)起一個(gè)Toast通知。

這里的原生模塊,是在RN項(xiàng)目的android文件夾下創(chuàng)建,如下圖:
這里的android文件夾可以用Android Studio打開(kāi),之后編輯,自我感覺(jué)方便的很。

Paste_Image.png
Paste_Image.png
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要求派生類(lèi)實(shí)現(xiàn)getName方法。這個(gè)函數(shù)用于返回一個(gè)字符串名字,這個(gè)名字在JavaScript端標(biāo)記這個(gè)模塊。這里我們把這個(gè)模塊叫做ToastAndroid,這樣就可以在JavaScript中通過(guò)React.NativeModules.ToastAndroid訪問(wèn)到這個(gè)模塊。譯注:RN已經(jīng)內(nèi)置了一個(gè)名為ToastAndroid的模塊,所以如果你在練習(xí)時(shí)完全照抄,那么運(yùn)行時(shí)會(huì)報(bào)錯(cuò)名字沖突!所以請(qǐng)?jiān)谶@里選擇另外一個(gè)名字!

  @Override
  public String getName() {
    return "ToastAndroidDemo";//我已經(jīng)改了
  }

譯注:模塊名前的RCT前綴會(huì)被自動(dòng)移除。所以如果返回的字符串為"RCTToastAndroidDemo",在JavaScript端依然可以通過(guò)React.NativeModules.ToastAndroidDemo訪問(wèn)到這個(gè)模塊。

一個(gè)可選的方法getContants返回了需要導(dǎo)出給JavaScript使用的常量。它并不一定需要實(shí)現(xiàn),但在定義一些可以被JavaScript同步訪問(wèn)到的預(yù)定義的值時(shí)非常有用。

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

要導(dǎo)出一個(gè)方法給JavaScript使用,Java方法需要使用注解@ReactMethod。方法的返回類(lèi)型必須為void。React Native的跨語(yǔ)言訪問(wèn)是異步進(jìn)行的,所以想要給JavaScript返回一個(gè)值的唯一辦法是使用回調(diào)函數(shù)或者發(fā)送事件(參見(jiàn)下文的描述)。

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

全部代碼:

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

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

參數(shù)類(lèi)型

下面的參數(shù)類(lèi)型在@ReactMethod注明的方法中,會(huì)被直接映射到它們對(duì)應(yīng)的JavaScript類(lèi)型。

Boolean -> Bool
Integer -> Number
Double -> Number
Float -> Number
String -> String
Callback -> function
ReadableMap -> Object
ReadableArray -> Array

參閱ReadableMapReadableArray。

注冊(cè)模塊

在Java這邊要做的最后一件事就是注冊(cè)這個(gè)模塊。我們需要在應(yīng)用的Package類(lèi)的createNativeModules方法中添加這個(gè)模塊。如果模塊沒(méi)有被注冊(cè),它也無(wú)法在JavaScript中被訪問(wèn)到。

package com.facebook.react.modules.toast;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
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 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;
  }

這個(gè)package需要在MainApplication.java文件的getPackages方法中提供。這個(gè)文件位于你的react-native應(yīng)用文件夾的android目錄中。具體路徑是: android/app/src/main/java/com/your-app-name/MainApplication.java。

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

為了讓你的功能從JavaScript端訪問(wèn)起來(lái)更為方便,通常我們都會(huì)把原生模塊封裝成一個(gè)JavaScript模塊。這不是必須的,但省下了每次都從NativeModules中獲取對(duì)應(yīng)模塊的步驟。這個(gè)JS文件也可以用于添加一些其他JavaScript端實(shí)現(xiàn)的功能。
ToastAndroidDemo.js文件代碼:

'use strict';

/**
 * This exposes the native ToastAndroid module as a JS module. This has a function 'show'
 * which takes the following parameters:
 *
 * 1. String message: A string with the text to toast
 * 2. int duration: The duration of the toast. May be ToastAndroid.SHORT or ToastAndroid.LONG
 */
import { NativeModules } from 'react-native';

// 下一句中的ToastAndroid即對(duì)應(yīng)上文
// public String getName()中返回的字符串
// 練習(xí)時(shí)請(qǐng)務(wù)必選擇另外的名字!

export default NativeModules.ToastAndroidDemo;

現(xiàn)在,在別處的JavaScript代碼中可以這樣調(diào)用你的方法:

import ToastAndroidDemo from './ToastAndroidDemo';
ToastAndroidDemo.show('Awesome', ToastAndroidDemo.SHORT);
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容