How to integrate react native code with an existing Android app?

nish abe
7 min readAug 9, 2022

I was tinkering with react native recently and learned a few things about setting up the dev environment and running it. This post details my experience. As a next step, I wanted to explore how we can integrate a react native code module into an existing Android app.

Here is the official documentation on how to do it. While it looks just fine, there are a few aspects about it that can be improvised. I have had a hard time making it work mostly because my exposure to react native and android development was limited. Also, the provided steps in the official documentation are not in sequential order. This post is my attempt to fix it.

All the steps below are tested and validated on a MacBook Pro M1 MAX running MacOS Monterey 12.5 with Xcode 13.4.1, Android studio Chipmunk, and Visual Studio Code 1.70.0.

Please make sure you have your dev environment set up correctly and you are able to run a react native app in an Android emulator or device. This is very important to continue with the next steps. You may refer to this post in case you need a little bit of help doing it.

The concept on a high level is to set up the react native dependencies in an accessible folder and then configure the Gradle file to point to it. Once that is done, use ‘ReactRootView’ to render the react native UI inside the native app.

Here are the main steps that need to be performed.

  1. Create a directory structure and set up the dependencies.
  2. Create a react native component
  3. Create an Android project
  4. Set up dependencies using Maven
  5. Add permissions in manifest
  6. Add code changes in the native Android app

Now the steps in detail:

1. Create a directory structure and set up the dependencies.

Create a root folder where the react native module and the existing (or new) android app coexist.

mkdir integratedReactNativeProject

Create a package.json file that will have the basic project information and other dependency details.

cd integratedReactNativeProject && touch package.json

Open the package.json which is just created in any text editors and add these lines:

{
"name": "MyReactNativeApp",
"version": "0.0.1",
"private": true,
"scripts": {
"start": "yarn react-native start"
}
}

If you don't have it already, install yarn

npm i yarn

Install the react and react-native packages using yarn.

yarn add react-native

You can see the log messages being added to the terminal. Search for the word ‘peer’ and note down the dependencies mentioned there. Here is an example:

Based on the above message we need to install babel/core, react18.0.0, babel/preset-env.

Either add each one of them one by one or install it once.

yarn add react@18.0.0  
yarn add @babel/core
yarn add react-native

or

yarn add @babel/preset-env && yarn add @babel/core && yarn add react@18.0.0

Once done, you can see that all these dependencies are added in package.json, and also a node_modules folder is added to the root directory.

2. Create a react native component

In this step, we will create a barebone react-native ‘hello’ world screen.

Create an index.js file inside the root folder, add the below lines and save it.

import React from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';

const HelloWorld = () => {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hello, World</Text>
</View>
);
};
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center'
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10
}
});

AppRegistry.registerComponent(
'MyReactNativeApp',
() => HelloWorld
);

3. Create an Android project

Assuming that you are still inside the root directory, we will create a subfolder called ‘android’ and within this newly created subfolder add an android project.

mkdir android

Please note that you can either create a brand-new android app and save it inside this subfolder or just copy and paste an existing android app project files here.

I am taking the first option which is creating a new app. Using Android Studio, create an ‘Empty activity’ project. I chose the language as Java.

Once the Gradle build is over, make sure to run this app in an emulator. I chose ‘Pixel 4 API 33’ which was added to my device manager. Ideally, you should see the screen with a ‘hello world!’ message.

4. Set up dependencies using Maven

In this step, we need to make changes in a few of the Gradle scripts so that they will point to the react native dependencies saved locally under the node_module folder.

Open the main Gradle file and add the below lines so that it looks like the screenshot.

allprojects {
repositories {
maven {
// All of React Native (JS, Android binaries) is installed from npm
url ("$rootDir/../node_modules/react-native/android")
}
maven {
// Android JSC is installed from npm
url("$rootDir/../node_modules/jsc-android/dist")
}
google()
jcenter()

}
}

Open the app Gradle file and add the below dependencies at the very bottom.

implementation "com.facebook.react:react-native:+" // From node_modules
implementation "org.webkit:android-jsc:+"

Now in the same file add the below line at the very bottom.

apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project)

After these two changes the file should look like this:

Open the gradle.properties file and add the below line:

android.enableJetifier=true

The file should look like this:

Go to settings.gradle and comment on the lines about ‘dependencyResolutionManagement’. Also, add the below line at the very bottom:

apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings)

Finally, the file should look like this:

Please run the Gradle sync now. The build should be a success and the app should be running as before.

5. Add permissions in manifest.xml

Let's make some changes to the manifest file.

Next, make sure you have Internet permission in your AndroidManifest.xml:

<uses-permission android:name=”android.permission.INTERNET” />

If you need to access the DevSettingsActivity add it to your AndroidManifest.xml:

<activity android:name=”com.facebook.react.devsupport.DevSettingsActivity” />

Inside <application > add this line:

android:usesCleartextTraffic="true"

Please make sure this setting is only for debug build not release build. This setting ensures that the app can communicate with the metro server while developing.

Also, change the ‘android:name’ from ‘.MainActivity’ to ‘.MyReactActivity’.

The manifest file should look like the below:

6. Add code changes in the native Android app

In the above step, we added the android name ‘MyReactActivity’. Let's add a new java file with the same name. This is to load the ‘MyReactActivity’ as the first screen when the app is launched.

Add the below lines of code in the newly added class.

package com.example.myapplication;
import android.app.Activity;
import android.os.Bundle;
import android.view.KeyEvent;

import com.facebook.react.PackageList;
import com.facebook.react.ReactInstanceManager;
import com.facebook.react.ReactPackage;
import com.facebook.react.ReactRootView;
import com.facebook.react.common.LifecycleState;
import com.facebook.react.modules.core.DefaultHardwareBackBtnHandler;
import com.facebook.soloader.SoLoader;

import java.util.List;


public class MyReactActivity extends Activity implements DefaultHardwareBackBtnHandler {
private ReactRootView mReactRootView;
private ReactInstanceManager mReactInstanceManager;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SoLoader.init(this, false);

mReactRootView = new ReactRootView(this);
List<ReactPackage> packages = new PackageList(getApplication()).getPackages();
// Packages that cannot be autolinked yet can be added manually here, for example:
// packages.add(new MyReactNativePackage());
// Remember to include them in `settings.gradle` and `app/build.gradle` too.

mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setCurrentActivity(this)
.setBundleAssetName("index.android.bundle")
.setJSMainModulePath("index")
.addPackages(packages)
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
// The string here (e.g. "MyReactNativeApp") has to match
// the string in AppRegistry.registerComponent() in index.js
mReactRootView.startReactApplication(mReactInstanceManager, "MyReactNativeApp", null);

setContentView(mReactRootView);
}

@Override
public void invokeDefaultOnBackPressed() {
super.onBackPressed();
}

}

Please note that the module name, ‘MyReactNativeApp’ here has to be the same as the one provided for AppRegistry.registerComponent() function of index.js file.

We are almost there. Assuming that you are still in the root folder, which is ‘integratedReactNativeProject’ in this case, run the below command:

yarn start

This will start the metro server. You should see something like this:

If everything is working as expected, you should be seeing the below screen when you run the app in Android Studio:

Hope this worked for you.

What is shown above is the simplest example. Please refer to the original documentation to know more about how to communicate between react world and the native world. Thanks for your time.

Miscellaneous issues:

Issue:

Solution:

In the terminal enter:

adb reverse tcp:8081 tcp:8081

This should resolve it.

Tip: You can show the debug menu on the Android emulator by executing this command:

adb shell input keyevent 82

--

--

nish abe

"Curious to know how things work and interested in making things work". https://nishabe.github.io/