Building custom Android modules for React Native

React Native applications have two sides (React's JavaScript side and the Android side). Assume these two sides are two sides of a river. Obviously, you need a bridge to cross this river. The React Native team built bridges for major things like Camera, Fingerprint, Scanner, etc... But they can’t cover every feature in Android. So, they gave us the tools to build our bridges.

I was using AsyncStorage to store the user preferences in my React Native app. But, the value stored by the AsyncStorage was not accessible from the Java layer of Android. So, I was starting my JavaScriptCore whenever my background service needed the user preferences. Then we decided to store the user preferences in Android’s Native Shared Preferences. I was searching for a corresponding bridge, on Google, and js.coach. But, there was no bridge to store the Shared Preferences from the React Native's layer. I decided to create one.

To view the final repo, click here

Creating the first module

First, create an AndroidManifest.xml file with your package name.

<manifest 
    xmlns:android=”http://schemas.android.com/apk/res/android"
    package=”in.sriraman.sharedpreferences”
>
</manifest>

We have to create the module by extending the ReactContextBaseModule. The first step is to override the name of the module.

@Override
public String getName(){
 return “SharedPreferences”;
}

I have already created a Java function to handle the data in Shared Preferences. Check the SharedHandler.java and SharedDataProvider.java to know how the Shared Preferences work inside the Java layer. Here, I have exposed three functions putSharedValue(key, value), getSharedValue(key) and clear(). These three functions can be accessed from our module.

We have to annotate the function with @ReactMethod for react methods.

@ReactMethod
public void setItem(String key, String value) {
    SharedHandler.init(getReactApplicationContext());
    SharedDataProvider.putSharedValue(key,value);
}

I have initialised the SharedHandler with the application context provided by getReactApplicationContext(). Then I can call the function to execute the particular task.

In the previous ReactMethod, We didn’t have the requirement to send back anything to the JavaScript layer. When we want to get the value from Shared Preferences, we should send a callback. Sending a callback can be done as follows:

@ReactMethod
public void getItem(String key, Callback successCallback){
     SharedHandler.init(getReactApplicationContext());
     String value = SharedDataProvider.getSharedValue(key);
     successCallback.invoke(value);
}

If we need to send a callback, then we should send an extra parameter for SuccessCallback. The callback can be sent back to the JavaScript layer using the successCallback.invoke() function. If you also need an error callback, you can just add an extra parameter in the ReactMethod.

After creating the React Module, we have to register the module. If you don’t register the module, it can’t be accessed from the JavaScript layer. Shared Preferences module is ready. You can directly consume the API in your application, or you can publish it to NPM.

If you are publishing it to NPM, users have to make a few changes to build the Java files inside their Android app. For a detailed explanation of the setup process, you can check this README.md file.

Write your comment…

3 comments

Hey! Nice introduction tutorial! Do you have any suggestion on the implementation/bridging of an Android service in React-Native?
I'm asking because I'm considering all the available options for making react-native-beacons-android works even when the app is killed.
Thanks ;)

Show all replies

React Native won't be alive in background. Obviously, the Event listener which we created in the React Native layer will also be destroyed. Communication might not be possible. Some people said that it is possible to start the Javascript core from the background. But, I never tried it. I'll let you know if I find some workaround.

Reply to this…

Hashnode is a friendly and inclusive dev community.
Come jump on the bandwagon!

  • 💬 Ask programming questions without being judged

  • 🧠 Stay in the loop and grow your knowledge

  • 🍕 More than 500K developers share programming wisdom here

  • ❤️ Support the growing dev community!

Create my profile

Thanks for this Sriraman, If I want to simply tweak an existing module e.g. react-native-maps to include map style capability, I need to import com.google.android.gms.maps.model.MapStyleOptions; in .java file that extends an Android MapView. However when running the app, it appears the MapStyleOptions is not in scope. Can you give me a clue, what are the steps to make this accessible in the Java? would i need to rebuild somehow from the original master? I don't see a reference to the other imports in this file elsewhere in the project for an example of how it was done.

There are two ways to do it. You can create a new react native module and add the react-native-maps as the dependency.

Reply to this…

Hi, I came here looking for an answer, I need to measure a custom component, but in react native you don't get the method .measure unless it's a native UI component as far as I understand from this:

The methods described here are available on most of the default components provided by React Native. Note, however, that they are not available on composite components that aren't directly backed by a native view. This will generally include most components that you define in your own app.

measure(callback)

... my question is: Making a Bridged (as in this tutorial) component will make it get .measure method? I'd like to know before learning to do this so at least i won't have false expectations...

thank you!