React native: communication with Android module

Time:2021-3-2

useToastAs an example. The function can be written in JavaScriptToastAndroid.show('Awesome', ToastAndroid.SHORT)To display a toast notification.

code:https://github.com/future-cha…

Create a native module

Create a class, inheritReactContextBaseJavaModule

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

Then you need to implement a methodgetName

@Override
  public String getName() {
    Return "anothertoasterandroid"; // toasterandroid cannot be returned, an error will be reported, or you need to manually specify to override the existing implementation of RN.
  }

This method has to be implemented. Its return value is the name of the JS part of react native when calling the module. In addition, if the string returned by this method containsRCTIf so, the RCT will be removed. That is, ifgetNameWhat is returned isRCTToastAndroidIf so, it is still used when calling JSToastAndroid

Next, implement theshowmethod.

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

be careful:Module to export methods to JS, then this method must be used@ReactMethodComments! And the return value must bevoid. If you want to return a value, you need to use a callback method or register an event. These will be discussed later.

The parameter type of the method

When adding parameters to methods exported to JS, only partial types (Java – > JavaScript) can be used:

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

Registration module

After registering the module, you can use it. If you don’t have a package class in your app, create one yourself. For example, in this example, you can create aToastReactPackageThis class implements theReactPackageInterface.

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

The name of each method in the class clearly indicates its role. What we export here is a module, so we need to implement itcreateNativeModulesmethod. Other methods only need to return an empty list. finalToastReactPackageClass is implemented as follows:

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

At the end of the dayMainApplicationOfgetPackagesMethod.

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 toast react package() // this sentence is used to register our anothertoast Android module
      );
    }
  };

Use modules in 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>
    );
  }
}

What’s not directly relevant is hidden. When you use it, you only need to introduce it into importNativeModules, followed bylet AnotherToastAndroid = NativeModules.AnotherToastAndroid;Extract our native module. The name of this module is the Android modulegetNameMethodAnotherToastAndroid

And then in theonPressCall in eventAnotherToastAndroidOfshowmethod.

So far, all the functions mentioned above have been realized.

Return constant

There is still one link to the operation of the previous content. Return the constant. You can see that there is such a call in JS code:

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

There is a saying:AnotherToastAndroid.LONG. To use long, there are short constants that are not used. You need the native module to return such constants.

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

methodgetConstantsIt’s a classReactContextBaseJavaModuleIs an optional method for returning constants. The returned content is the dictionaryMap<String, Object>

Now demo is ready to run.

Callback method

Previously, to return a value to JS, you need to use a callback method. Now let’s see how this can be achieved in native

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

Add a method to get the current thread in toast module. Android’s export callback method still looks a little strange. Originally, a callback should return two parameters: an error and a result. Two are used hereCallbackMaybe it’s also a condition.

Let’s see how JS works

  <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

The drawback is obvious. So most of the time will choose to usePromise. Plus the current fashionasync-awaitMore people use promise.

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

Let’s see how to use promise’s:

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

This knowledge is enough in general use. If you need more complex content, you can view itOfficial documents. I will also complete this part later.