How to setup communication between Java and JavaScript in React Native?

By Prajwal Haniya

Techletter #37 | July 5, 2023

Writing a native module can actually give a better performance to your app. It’s not always important, but sometimes when you are implementing something which is not available within React native ecosystem you need the help of a native language like Java.

Believe me! It’s been years since I have written anything in Java. I have been using only JavaScript and hope will be continuing with it. But, writing Java can be necessary. For example, I faced a lot of problems while searching for a React native library that can work efficiently with the call logs. You will find a few of them, but there comes a time when they aren’t solving all the problems related to calls. So, you need to write your own module for achieving the task that you want.

Here, I will be showing how you can implement your module and call the method present in the Java module in the React native file.

android/app/src/main/java/com/your_package/ReactCustomModule.java

package com.YOUR_PACKAGE_NAME;

import android.annotation.SuppressLint;
import android.provider.Settings;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactMethod;

public class ReactCustomModule extends ReactContextBaseJavaModule {
    private static ReactApplicationContext reactContext;
    ReactOneCustomMethod(ReactApplicationContext context) {
       super(context);
        reactContext = context;
    }

    @NonNull
    @Override
    public String getName() {
        return "ReactCustomModule";
    }

    @ReactMethod
    public void getSimpleName(Promise response) {
        try {
            response.resolve("Example Package");
        } catch (Exception e) {
            response.reject("Error", e);
        }
    }
}

All Java/Kotlin native modules in Android need to implement the getName() method. This method returns a string, which represents the name of the native module. The native module can then be accessed in JavaScript using its name. Once you write the above class, next you need to register the module that you have written.

android/app/src/main/java/com/your_package/ReactCustomPackage.java

package com.YOUR_PACKAGE_NAME;

import androidx.annotation.NonNull;

import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class ReactCustomPackage implements ReactPackage {

    @NonNull
    @Override
    public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();

        modules.add(new ReactCustomModule(reactContext));

        return modules;
    }

    @NonNull
    @Override
    public List<ViewManager> createViewManagers(@NonNull ReactApplicationContext reactContext) {
        return Collections.emptyList();
    }
}

Now go to the MainApplication.java in the same folder

package com.YOUR_PACKAGE_NAME;
// Not Necessary
import com.YOUR_PACKAGE_NAME.ReactCustomPackage;

import android.app.Application;
import android.content.res.Configuration;
import androidx.annotation.NonNull;

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

import expo.modules.ApplicationLifecycleDispatcher;
import expo.modules.ReactNativeHostWrapper;

import java.util.List;

public class MainApplication extends Application implements ReactApplication {

  private final ReactNativeHost mReactNativeHost =
    new ReactNativeHostWrapper(this, new DefaultReactNativeHost(this) {
      @Override
      public boolean getUseDeveloperSupport() {
        return BuildConfig.DEBUG;
      } 

      @Override
      protected List<ReactPackage> getPackages() {
        @SuppressWarnings("UnnecessaryLocalVariable")
        List<ReactPackage> packages = new PackageList(this).getPackages();
        // Packages that cannot be autolinked yet can be added manually here, for example:
        // packages.add(new MyReactNativePackage());
        packages.add(new ReactCustomPackage());
        return packages;
      }

      @Override
      protected String getJSMainModuleName() {
        return "index";
      }

      @Override
      protected boolean isNewArchEnabled() {
        return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED;
      }

      @Override
      protected Boolean isHermesEnabled() {
        return BuildConfig.IS_HERMES_ENABLED;
      }
  });

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

  @Override
  public void onCreate() {
    super.onCreate();
    SoLoader.init(this, /* native exopackage */ false);
    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      // If you opted-in for the New Architecture, we load the native entry point for this app.
      DefaultNewArchitectureEntryPoint.load();
    }
    ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager());
    ApplicationLifecycleDispatcher.onApplicationCreate(this);
  }

  @Override
  public void onConfigurationChanged(@NonNull Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    ApplicationLifecycleDispatcher.onConfigurationChanged(this, newConfig);
  }
}

Now you can use the Java module that you have written directly in your React native file

import { NativeModules } from "react-native";

const {ReactCustomModule} = NativeModules;

export const nativeMethod = async () => {
    try {
        const data = await ReactCustomModule.getSimpleName();
        // Data returned from the java module to make use of it here in react code
        console.log({ data });
    } catch (error) {
        console.log(error);
    }
}

Resources: