Integrating React Native with existing Android app

Note : This article has been written after testing under the following environment. 
1) React version : 16.0.0-alpha.12
2) React Native version : ^0.46.1
The guide might change with the version change. So if this guide doesn’t work for your environment please mention the errors in the comments below or send me a mail on rajsuvariya@gmail.com

Pre-requisite:
  1. React Native setup in your system. If you don’t have then please follow this guide
    Note : Please do not proceed if you are not able to create and run a project without any errors with the above guide.
  2. Android Studio setup with the latest version of JDK.
  3. An existing Android project ( for this article we will be using AwesomeApp ).

Step 1 : Re-structuring the existing project

This step is not the mandatory step for integrating react native into the existing project. But this step is important as it separates the Android Native and React Native code from each other.

Create a folder /android inside the root directory of the existing project ( in AwesomeApp folder).

After this step, your structure looks like this.

android folder created in root project folder

Now move everything to the android folder using cut and paste. So the existing project files will live at the path /AwesomeApp/android/<project files>.

Project structure after moving everything to /android folder

Now, you can open /AwesomeApp/android in Android Studio to write the Android Native code.

Step 2 : Install JavaScript dependencies

Now, move back to root directory /AwesomeApp and create a file named package.json and paste the following code inside it.

{
"name": "AwesomeReactApp", //name of your react app
"version": "0.0.1",
"private": true,
"scripts": {
"start": "node node_modules/react-native/local-cli/cli.js start"
}
}

It’s time to install the react and react-native packages in our project. So, open a terminal inside the root directory /AwesomeApp and run the following command.

$ npm install --save react@16.0.0-alpha.12 react-native

This will create a new /node_modules folder inside the root directory. This folder contains all the javascript packages required to run the app.

Step 3 : Integrating React Native in the project

Open the AwesomeApp/android folder in Android Studio.

Configuring Maven

Add the following dependency to the /app/build.gradle of the existing project.

$ compile "com.facebook.react:react-native:+"

If you want to work with the specific version of the react native then replace the + with the exact version.

Add an entry for the local React Native maven directory to build.gradle. Be sure to add it to the "allprojects" block and add it to android/build.gradle, not to android/app/build.gradle

maven {
url "<PATH TO YOUR ROOT DIRECTORY>/AwesomeApp/node_modules/react-native/android"
}

So, the android/build.gradle looks like this now.

allprojects {
repositories {
jcenter()
maven { url 'https://maven.google.com' }
maven {
url "/home/user/Workspace/react-native-projects/AwesomeApp/node_modules/react-native/android"
}

}
}

Now, sync the project in Android Studio. If you get any error please resolve it before proceeding to next step.

Adding permissions

Open AndroidManifest.xml in Android Studio which can be located at path android/app/src/main/AndroidManifest.xml. Add the following permission to it.

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

After adding the permission, AndroidManifest.xml looks like this

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rajsuvariya.awesomeapp">

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

<application
android:name=".IfaApp"
android:allowBackup="true"
...

If you want to access DevSettingsActivity (used to access developer settings from mobile app) then add it to AndroidManifest.xml

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

This is only really used in dev mode when reloading JavaScript from the development server, so you can strip this in release builds if you need to.

Code Integration

Now, we will add the React Native code and create an activity in native Android which will render the React Native code.

Create index.android.js file inside the root directory /AwesomeApp. This is the entry point of the React Native app. You can read about it more on React Native official documentation. We will be adding a very simple code in React Native which will just say “Hey! How are you?” to the user.

Add the following code inside index.android.js

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

class HelloUser extends React.Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.hello}>Hey! How are you?</Text>
</View>
)
}
}
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
},
hello: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
});

AppRegistry.registerComponent('AwesomeReactApp', () => HelloUser);

Make sure you write the same project name in AppRegistry.registerComponent() as written in package.json.

If you are targeting Android API level ≥ 23 then you need to take a permission from user for opening the React Native app from your native Android app, as after API level 23 new dynamic permission for opening other apps has been introduced.

For requesting the permission add the following code either inside the onCreate() of the first activity of the app or inside the onCreate() of the activity which will open the React Native activity.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
}
}

After adding this permission request, the onCreate() looks like this.

private static final int OVERLAY_PERMISSION_REQ_CODE = 1212;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
Uri.parse("package:" + getPackageName()));
startActivityForResult(intent, OVERLAY_PERMISSION_REQ_CODE);
}
}
...
}

Finally, the onActivityResult() method (as shown in the code below) has to be overridden to handle the permission Acceptance or Denial from the user.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == OVERLAY_PERMISSION_REQ_CODE) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (!Settings.canDrawOverlays(this)) {
Toast.makeText(this, "You cannot open the React Native app as you have denied the permission", Toast.LENGTH_SHORT).show();
}
}
}
}

Now, we will write the MyReactActivity which will render the React Native App. So, create a new Activity MyReactActivity using Android Studio and add the following code inside it.

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

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

mReactRootView = new ReactRootView(this);
mReactInstanceManager = ReactInstanceManager.builder()
.setApplication(getApplication())
.setBundleAssetName("index.android.bundle")
.setJSMainModuleName("index.android")
.addPackage(new MainReactPackage())
.setUseDeveloperSupport(BuildConfig.DEBUG)
.setInitialLifecycleState(LifecycleState.RESUMED)
.build();
mReactRootView.startReactApplication(mReactInstanceManager, "AwesomeReactApp", null);

setContentView(mReactRootView);
}

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

@Override
protected void onPause() {
super.onPause();

if (mReactInstanceManager != null) {
mReactInstanceManager.onHostPause(this);
}
}

@Override
protected void onResume() {
super.onResume();

if (mReactInstanceManager != null) {
mReactInstanceManager.onHostResume(this, this);
}
}

@Override
protected void onDestroy() {
super.onDestroy();

if (mReactInstanceManager != null) {
mReactInstanceManager.onHostDestroy(this);
}
}

@Override
public void onBackPressed() {
if (mReactInstanceManager != null) {
mReactInstanceManager.onBackPressed();
} else {
super.onBackPressed();
}
}

@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_MENU && mReactInstanceManager != null) {
mReactInstanceManager.showDevOptionsDialog();
return true;
}
return super.onKeyUp(keyCode, event);
}

}

You will get a lot of errors after pasting this code inside MyReactActivity, as the packages used inside the activity are not imported. To import all the packages hover over them one by one and press Alt + Enter. Make sure you import BuidConfig from your package and not from ...facebook...package.

Also, change the theme of this activity as some of the components use this special theme. So, inside the AndroidManifest.xml update/add the following code.

<activity
android:name=”.react.MyReactActivity”
android:label=”@string/app_name”
android:theme=”@style/Theme.AppCompat.Light.NoActionBar”></activity>

Opening React App

We need to open the MyReactNative activity to launch the React Native app. To do so write the following code wherever you want (according to the business logic of your app)

Intent intent = new Intent(this, MyReactActivity.class);
startActivity(intent);

Run and Test

We are ready to run the app and test it out. To start the React Native server run the following command inside the root directory /AwesomeApp.

$ npm start

Now, run the app from Android Studio as usual and test it out. You must see this screen.


Mark ❤ if you like this blog and follow me to get notification of my future blogs on React Native and Android.

My other blog(s) on ReactNative:

#ReactNative #Android #HybridApps