React Native填坑之旅--與Android模塊通訊

使用Toast做爲例子。實現的功能是能夠在JavaScript裏寫ToastAndroid.show('Awesome', ToastAndroid.SHORT)來顯示一個Toast通知。javascript

代碼:https://github.com/future-cha...html

建立一個原生模塊

建立一個類,繼承ReactContextBaseJavaModulejava

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

以後須要實現一個方法getNamereact

@Override
  public String getName() {
    return "AnotherToastAndroid"; // 不能返回ToastAndroid,這個會報錯,或者須要手動指定覆蓋RN已有的實現。
  }

這個方法必須實現。它的返回值是React Native的js部分調用模塊時的名稱。另外,若是這個方法返回的字符串包含RCT的話,那麼RCT會被去掉。也就是,若是getName返回的是RCTToastAndroid的話,在js調用的時候仍是使用ToastAndroidandroid

接下來實現show方法。git

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

注意:模塊要導出方法給js使用,那麼這個方法上必須使用@ReactMethod註解!而且返回值必須爲void。若是要返回值的話,須要使用回調方法或者註冊事件。這些下文會講到。github

方法的參數類型

在導出給js的方法中添加參數的時候,只能使用部分類型(java -> javascript):react-native

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

註冊模塊

註冊模塊以後就可使用。若是你的App裏沒有Package類,那就本身建立一個。好比本例,就能夠建立名爲ToastReactPackage的Package類,該類實現ReactPackage接口。promise

public class ToastReactPackage implements ReactPackage {
  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    return null;
  }

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

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return null;
  }
}

類中每一個方法的名稱已經明確的代表了其自己的做用。咱們這裏導出的是一個模塊,因此須要實現createNativeModules方法。其餘的方法只要返回一個空列表就能夠。最後的ToastReactPackage類的實現是:async

public class ToastReactPackage implements ReactPackage {
  @Override
  public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
    List<NativeModule> modules = new ArrayList<>();
    modules.add(new ToastModule(reactContext));

    return modules;
  }

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

  @Override
  public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
    return Collections.emptyList();
  }
}

最後在MainApplicationgetPackages方法裏註冊Package。

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 ToastReactPackage() // 這一句用來註冊咱們的AnotherToastAndroid模塊
      );
    }
  };

在React Native中使用模塊。

import {
  //...
  NativeModules,
  PixelRatio,
} from 'react-native';

let AnotherToastAndroid = NativeModules.AnotherToastAndroid;

export default class mobike extends Component {
  render() {
    return (
      <View style={styles.container}>
        <TouchableOpacity style={styles.button} onPress={() => {
          AnotherToastAndroid.show('Another Toast', AnotherToastAndroid.LONG);
        }}>
          <Text style={{ textAlign: 'center', }}>
            Show Toast
          </Text>
        </TouchableOpacity>
      </View>
    );
  }
}

不直接相關的內容就隱藏掉了。使用的時候只要在import中引入NativeModules,以後在let AnotherToastAndroid = NativeModules.AnotherToastAndroid;提取咱們的原生模塊。這個模塊的名字就是在Android模塊getName方法裏返回的名稱AnotherToastAndroid

以後在TouchableOpacity的onPress事件中調用AnotherToastAndroidshow方法。

至此,咱們以前說的功能就都實現了。

返回常量

前面的內容要運行起來還差這個一環。返回常量,你看到js代碼裏有這樣的調用:

AnotherToastAndroid.show('Another Toast', AnotherToastAndroid.LONG);

有這麼一句:AnotherToastAndroid.LONG。要使用LONG,還有沒有用到的SHORT常量,須要原生模塊返回這樣的常量。

@Nullable
  @Override
  public Map<String, Object> getConstants() {
//    return super.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;
  }

方法getConstants是類ReactContextBaseJavaModule的一個可選方法,專門用來返回常量。返回的內容就是字典Map<String, Object>

如今Demo能夠運行起來了。

回調方法

前文說道,要返回值給js就須要用回調方法。如今看看在原生裏如何實現這一點:

@ReactMethod
  public void currentThreadName(Callback errorCallback, Callback successCallback) {
    try {
      String tn = Thread.currentThread().getName();
      successCallback.invoke(tn);
    } catch(Exception e) {
      errorCallback.invoke(e.getMessage());
    }
  }

在Toast模塊里加了一個獲取當前線程的方法。Android的這個導出回調方法看起來仍是有點奇怪。原本應該是一個回調返回兩個參數:一個error,一個結果。這裏用了兩個Callback,可能也是條件限制吧。

看看js如何使用:

<Button
    style={{ marginTop: 10, }}
    title='use callback'
    pressHandler={
      () => {
        AnotherToastAndroid.currentThreadName((msg) => console.log(`error message ${msg}`)
          , (threadName) => {
            Alert.alert('Thread Name', `thread nane: ${threadName}`, null);
          });
      }}
  />

Promise

回調缺點很明顯。因此多數的時候都會選擇使用Promise。再加上如今流行的async-await就更多的人使用Promise了。

@ReactMethod
  public void currentThreadNameByPromise(Promise promise) {
    try {
      String tn = Thread.currentThread().getName();
      promise.resolve(tn);
    } catch (Exception e) {
      promise.reject("Thread Error", e);
    }
  }

來看看如何使用Promise的:

<Button
    style={{ marginTop: 10, }}
    title='use Promise'
    pressHandler={
      () => {
        AnotherToastAndroid.currentThreadNameByPromise().then((threadName) =>
          Alert.alert('Thread Name', `thread nane: ${threadName}`, null)
        ).catch(err => Alert.alert('Thread Name', `get thread nane error: ${err.message}`, null));
      }}
  />

這些知識在通常的使用中就足夠了,若是須要更復雜的內容能夠查看官方文檔。我也會在以後補齊這部分的內容。

相關文章
相關標籤/搜索