Native Modules for React Native Android
A guide to help you across the React Native bridge
When developing an Android application with React Native you may need to access an API that doesn’t yet have a corresponding React Native module. This can easily be done by writing your own native module in Java and selectively exposing its public API to React Native. Let’s give it a try!
What We’ll Be Making
At the time of this writing, React Native contains the component ImagePickerIOS but no corresponding ImagePicker component for Android. We’re going to build our own simple ImagePicker component that roughly mirrors the behavior of ImagePickerIOS.
Writing a native module for Android involves the following steps:
- Create a ReactPackage, a wrapper object grouping many modules (both native and JavaScript) together, and include it in the getPackages method of MainActivity.
- Create a Java class extending ReactContextBaseJavaModule that implements the desired functionality and register it with our ReactPackage.
- Override the getName method in the aforementioned class. This will be the name of the native module in JavaScript.
- Expose desired public methods to JavaScript by annotating them with @ReactMethod.
- Finally, import the module from NativeModules in your JavaScript code and call the methods.
Let’s see what this looks like in practice.
Creating a ReactPackage
Fire up AndroidStudio and navigate to MyApp/android/app/src/main/java/com/myapp/MainActivity.java. It should look something like this:
We’re going to be optimistic and include the package we haven’t yet defined.
Now let’s actually define that package. We’ll create a new directory for it called imagepicker and include the following in ImagePickerPackage:
Now that we’ve created the package and included it in the MainActivity we’re ready to start defining our module.
Creating a ReactContextBaseJavaModule
We’ll start by creating the ImagePickerModule class, and extending ReactContextBaseJavaModule.
That’s a good start, but in order for React Native to find our module in NativeModules we’ll need to override the getName method.
We now have a fully functional (if totally useless) native module that we can import in our JavaScript code. Let’s make it do something a bit more interesting.
Exposing Methods
ImagePickerIOS defines an openSelectDialog method that takes a config object and success and cancel callbacks. Let’s define a similar method in ImagePickerModule.
Here we import Callback and ReadableMap from React Native bridge which correspond to JavaScript object and function respectively. We annotate the method with @ReactMethod exposing it to JavaScript as part of the ImagePicker module. In the body of the method we get the current activity or call the cancel callback if it doesn’t exist. We now have a working method, but it doesn’t do anything interesting yet. Let’s add to it to make it open the image gallery.
First, we set the callbacks as instance variables for reasons that will become clear later. Then we create our Intent, configure it and pass it to startActivityForResult. Finally, we wrap the whole thing in a try/catch block to handle any exceptions we might run into.
You should now see an image gallery when you call openSelectDialog on ImagePicker. However when you select an image the gallery will just dismiss itself without doing anything. In order to actually return any image data we’ll need to handle the activity result in our module.
First we’ll need to add an activity event listener to our react context:
Now that we can listen to activity events we can handle onActivityResult and return the image data we want.
With this in place we should now be receiving the image URI in the success callback of our call to openSelectDialog.
To further mirror the behavior of ImagePickerIOS, we could build on the configuration options allowing users to pick images, video, or both as well as support opening the camera directly. As these features would be building on the same concepts already demonstrated, we’ll leave them as an exercise to the reader.
Special Thanks
I could not have done this without the help and support of Infinite Red Technical Lead Gant Laborde. His intimate knowledge of toast saved my bacon.
About Ryan Linton
Ryan Linton is a Senior Software Engineer at Infinite Red who enjoys working closely with clients while bringing their projects to life. When not tweaking styles and queries he can often be found traveling the world or desperately trying to make a dent in his ever growing reading list.