It’s time to reboot banking. Ferocia is working on Up, an alternative to your current bank.

Rendering React Native within a portion of your Android App using Android Fragments.

Ferocia is proud to have built two world-class mobile banking platforms for nearly a decade. We started with the complete overhaul of an existing banking platform for Bendigo Bank, one of the biggest banks in Australia. We did this with separate iOS and Android apps, along with a desktop implementation. For our latest endeavour, Up, we went all-in on React Native and never looked back.

Based on that experience, we’re now looking to uplift the tech in our flagship iOS and Android apps to be more in line with Up. How are we going to move to React Native without all the risk associated with a rebuild? In true agile fashion, we’re not exactly sure how every step in the journey will go yet, but in this blog post I can at least tell you how we will start!

Bendigo Banks native Android app (left) and Up Bank's React Native app (right)

What are the pros and cons of this migration?

There are two main pros. The first is the appeal of a mono-repo which can hold the entire platform’s code - this means Android, iOS and web. The second is that our team consists of more JavaScript (JS)/React developers than native ones. The streamlining of our repos into one and the shift away from native to JS code will inevitably lead to a better developer experience (for our team specifically). As well as the ability for the same developers to work on both native and web code using the same tech.
An obvious con is the fact that a lot of developer time and energy will need to go into these migrations. Rebuilding native code in React Native is no mediocre feat. There will be hurdles and headaches, but in our opinion the pros outweigh the cons.

Why not just follow the existing React Native docs and migrate your native Android app on an Activity by Activity basis?

In order to achieve this move to React Native, we need to keep each migration task as small as possible. This is so we can keep testing self-contained, and so we can easily park this migration work if more pressing initiatives arise. For some people, migrating entire activities at a time makes sense. You can read about how to do that here: React Native Docs - Integration with Existing Apps.

Why are you writing about this?

A lot of time went into researching if this was possible, and how best to achieve it. React Native did not include documentation on this process. We are hoping to save anyone else who is interested in this approach some of that hot commodity - time.
This blog post will outline how to render React Native within an Android Fragment, offering a step-by-step guide. If you prefer reading through the code and implementing a solution on your own, you can take a look at this example repo provided by one of our finest devs: https://github.com/ferocia/react-native-android-fragment-example. This post assumes you have basic knowledge of how Android apps work and have one set up.


1. Add React Native to your app

Follow the React Native guide for Integration with Existing Apps until the Code integration - The Magic: RCTRootView section. If you create the root React Native file as index.js (instead of index.android.js) you will need to override getJSMainModule() to return "index";. This is recommended if you want to share code between iOS and Android.

2. Integrating your App with a React Native Fragment

Let’s get to what we’re all here for, rendering React Native within an Android Fragment. This component may be termed a “screen” or “fragment”. It will be analogous to an Android fragment, and likely contains child components. In our example we placed all of these fragments in a /fragments folder and the child components used to compose the fragment were in a /components folder.

We need to implement ReactApplication in our main Application Java class. If you have created a new project from Android Studio with a default activity, you will need to create a new class e.g. MyReactApplication.java. If it is an existing class you can find this Application class in your AndroidManifest.xml file. Under the <application /> tag you should see a property android:name=".MyReactApplication". The value provided is the class you want to add this implementation to and provide the required methods.
Ensure your main Application Java class implements ReactApplication:

public class MyReactApplication extends Application implements ReactApplication {...}

Override the required methods getUseDeveloperSupport, getPackages and getReactNativeHost:

public class MyReactApplication extends Application implements ReactApplication {
    @Override
    public void onCreate() {
        super.onCreate();
        SoLoader.init(this, false);
    }

    private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) {
// This tells React Native when it should render its error windows in the app. This renders it when the app is in its DEBUG flavour.
        @Override
        public boolean getUseDeveloperSupport() {
            return BuildConfig.DEBUG;
        }

        protected List<ReactPackage> getPackages() {
            List<ReactPackage> packages = new PackageList(this).getPackages();
// Packages that cannot be autolinked yet can be added manually here
            return packages;
        }
    };

    @Override
    public ReactNativeHost getReactNativeHost() {
        return mReactNativeHost;
    }
}

If you are using Android Studio, use Alt + Enter to add all missing imports in your class. Alternatively these are the required imports to include manually:

import android.app.Application;

import com.facebook.react.PackageList;
import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.soloader.SoLoader;

import java.util.List;

Perform a “Sync Project files with Gradle” operation.

Step 3. Add a FrameLayout for the React Native Fragment

We can now add the React Native Fragment to an Android Activity. For a new project this will be MainActivity but it can be added to any Activity and more fragments can be added to additional Activities as you integrate more React Native components into your app.
First add the React Native Fragment to our Activity’s layout. For example main_activity.xml in the res/layouts folder.
Add a <FrameLayout> with an id, width and height. This is the layout we will find by id and render our React Native Fragment into.

<FrameLayout
    android:id="@+id/reactNativeFragment"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

Step 4. Add a React Native Fragment to the FrameLayout

To add your React Native Fragment to your layout we need to have an Activity, this is because Fragments must be rendered within an Activity. As mentioned in a new project this will be MainActivity. In this Activity we will add a button and an event listener, on button click we will render out React Native Fragment. It does not need to be rendered on a button click, for example you can render it in the onCreate method of that Activity (or on any other event).

First modify your Activity layout to add the button:

<Button
    android:layout_margin="10dp"
    android:id="@+id/button"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:text="Show react fragment" />

Now back in our Activity class e.g. MainActivity.java we need to add an OnClickListener for the button, instantiate our ReactFragment and add the fragment to the frame layout.
Add to the top of our Activity the button field:

private Button mButton;

Now update your Activity’s onCreate method as follows:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main_activity);

    mButton = findViewById(R.id.button);
    mButton.setOnClickListener(new View.OnClickListener() {
        public void onClick(View v) {
            Fragment reactNativeFragment = new ReactFragment.Builder()
                    .setComponentName("HelloWorld")
                    .setLaunchOptions(getLaunchOptions("test message"))
                    .build();

            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.reactNativeFragment, reactNativeFragment)
                    .commit();

        }
    });
}

In the code above Fragment reactNativeFragment = new ReactFragment.Builder() creates our ReactFragment and getSupportFragmentManager().beginTransaction().add() will add the Fragment to our Frame Layout.
If you are using a starter kit for React Native, replace the "HelloWorld" string with the one in your index.js or index.android.js file (it’s the first argument to the AppRegistry.registerComponent() method).
Add the getLaunchOptions method which will allow you to pass props through to your component. This is optional and you can remove setLaunchOptions if you don’t need to pass any props.

private Bundle getLaunchOptions(String message) {
    Bundle initialProperties = new Bundle();
    initialProperties.putString("message", message);
    return initialProperties;
}

Add all missing imports in your Activity class. Be careful to use your package’s BuildConfig and not the one from the facebook package! Alternatively these are the required imports to include manually:

import android.app.Application;

import com.facebook.react.ReactApplication;
import com.facebook.react.ReactNativeHost;
import com.facebook.react.ReactPackage;
import com.facebook.react.shell.MainReactPackage;
import com.facebook.soloader.SoLoader;

Perform a “Sync Project files with Gradle” operation.

Step 5. Test your integration

Make sure you run yarn to install your react-native dependencies and run yarn native to start the metro bundler. Run your android app in Android Studio and it should load the JavaScript code from the development server and display it in your React Native Fragment in the Activity.

Step 6. Additional setup - Native modules

We may need to call out to existing Java code from our react component being used in the Fragment. Native modules will allow you to call to native code, allowing you to run methods in your native app from within React Native. Follow the setup here: React Native Docs - Native Modules.




So there you have it, React Native running within a portion of your existing Android Activity. While eventually we plan to run all our native code within React Native, being able to run React Native alongside our currently existing native code is a great way to begin the migration process. This helps us keep our pieces of work smaller, makes testing easier, and allows us to park this work when more pressing initiatives arise.