author

Kien Duong

April 24, 2022

How to create a React Native Android module

Today, I’m going to show you how to create a native android module for React Native. Please follow the previous blog to understand the custom module folder (How to create a React Native iOS module).

 

1. Create Android library

Here is the structure for android module (ignore the build folder)

react native android 1

    // build.gradle
buildscript {
    if (project == rootProject) {
        repositories {
            google()
            mavenCentral()
            jcenter()
        }

        dependencies {
            classpath 'com.android.tools.build:gradle:3.5.3'
        }
    }
}

apply plugin: 'com.android.library'

def safeExtGet(prop, fallback) {
    rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
}

android {
    compileSdkVersion safeExtGet('compileSdkVersion', 29)
    defaultConfig {
        minSdkVersion safeExtGet('minSdkVersion', 16)
        targetSdkVersion safeExtGet('targetSdkVersion', 29)
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
        }
    }
    lintOptions {
        disable 'GradleCompatible'
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

repositories {
    mavenLocal()
    maven {
        // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm
        url("$rootDir/../node_modules/react-native/android")
    }
    google()
    mavenCentral()
    jcenter()
}

dependencies {
    //noinspection GradleDynamicVersion
    implementation "com.facebook.react:react-native:+"  // From node_modules
}

  

Inside android folder, we need to create 3 new files:

  • src/main/java/com/reactnativedemomodule/DemoModuleModule.java
  • src/main/java/com/reactnativedemomodule/DemoModulePackage.java
  • src/main/AndroidManifest.xml

reactnativedemomodule is your package name, DemoModule is your module name.

    // AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.reactnativedemomodule">
</manifest>

  

In AndroidManifest.xml file, you must define your package name as com.{your-package-name}. As the previous blog, we’re going to write the logic to detect the current location. The first thing, you have to add the logic for DemoModulePackage.java file.

    // DemoModulePackage.java
package com.reactnativedemomodule;

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 DemoModulePackage implements ReactPackage {
    @NonNull
    @Override
    public List<NativeModule> createNativeModules(@NonNull ReactApplicationContext reactContext) {
        List<NativeModule> modules = new ArrayList<>();
        modules.add(new DemoModuleModule(reactContext));
        return modules;
    }

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

  

The next thing, add the logic for DemoModuleModule.java file. Here is the basic code for module file without any methods. Your module must extend ReactContextBaseJavaModule of React core. After that, we will handle the logic to detect current location. Like iOS module, we will return an string array by promise.

    // DemoModuleModule.java
package com.reactnativedemomodule;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.module.annotations.ReactModule;

@ReactModule(name = DemoModuleModule.NAME)
public class DemoModuleModule extends ReactContextBaseJavaModule {
    public static final String NAME = "DemoModule";

    public DemoModuleModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

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

  
    // DemoModuleModule.java
package com.reactnativedemomodule;

import androidx.annotation.NonNull;

import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableNativeArray;
import com.facebook.react.bridge.PromiseImpl;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReadableArray;
import com.facebook.react.module.annotations.ReactModule;
import com.facebook.react.modules.permissions.PermissionsModule;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.location.Criteria;
import android.widget.Toast;
import android.Manifest;
import java.util.ArrayList;
import android.os.Bundle;

@ReactModule(name = DemoModuleModule.NAME)
public class DemoModuleModule extends ReactContextBaseJavaModule {
    public static final String NAME = "DemoModule";

    private static final long MIN_TIME_UPDATE = 1000 * 60 * 1;
    private static final float MIN_DISTANCE_UPDATE = 1;

    private final LocationListener locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) { }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) { }

        @Override
        public void onProviderEnabled(String provider) { }

        @Override
        public void onProviderDisabled(String provider) { }
    };


    public DemoModuleModule(ReactApplicationContext reactContext) {
        super(reactContext);
    }

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

    @ReactMethod
    public void getCurrentLocation(Promise promise) {
        Callback onSuccess = new Callback() {
            @Override
            public void invoke(Object... args) {
                LocationManager locationManager = (LocationManager) getReactApplicationContext().getSystemService(Context.LOCATION_SERVICE);

                Criteria criteria = new Criteria();
                criteria.setAccuracy(Criteria.ACCURACY_FINE);
                String provider = locationManager.getBestProvider(criteria, false);

                boolean isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
                boolean isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

                if (!isGPSEnabled && !isNetworkEnabled) {
                    showToast("No network provider is enabled");
                } else {
                    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_UPDATE, MIN_DISTANCE_UPDATE, locationListener);

                    if (locationManager != null) {
                        Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        
                        if (location != null) {
                            String latitude = String.format("%.4f", location.getLatitude());
                            String longitude = String.format("%.4f", location.getLongitude());

                            WritableNativeArray result = new WritableNativeArray();
                            result.pushString(latitude);
                            result.pushString(longitude);
                            promise.resolve(result);
                        }
                    }                 
                }
            }
        };

        Callback onError = new Callback() {
            @Override
            public void invoke(Object... args) {
                showToast("Permissions are denied");
            }
        };

        PermissionsModule permission = getReactApplicationContext().getNativeModule(PermissionsModule.class);
        permission.requestPermission(Manifest.permission.ACCESS_FINE_LOCATION, new PromiseImpl(onSuccess, onError));
    }

    public void showToast(String message) {
        Toast.makeText(
            getReactApplicationContext(),
            message,
            Toast.LENGTH_LONG
        ).show();
    }
}

  

2. Add your module to Android project

In order to use your custom module, you have to config some files manually. Go to android folder of your React Native project.

  • settings.gradle
  • app/build.gradle
  • app/src/main/java/com/reactnativedemo/MainApplication.java

2.1. In settings.gradle file, include your custom module path

    // settings.gradle
...
include ':react-native-demo-module'
project(':react-native-demo-module').projectDir = new File(rootProject.projectDir, '../custom-modules/react-native-demo-module/android')
...
  

2.2. In build.gradle file, find dependencies section and add a new line

    // build.gradle
dependencies {
    ...
    implementation project(':react-native-demo-module')
}
  

2.3. In MainApplication.java file, import your package & add your package to getPackages function

    // MainApplication.java
...
import com.reactnativedemomodule.DemoModulePackage;
...


...
@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 DemoModulePackage());
  return packages;
}
...
  

Because we will detect the current location in this logic so we have to add the location permission to app/src/main/AndroidManifest.xml file

<uses-permission android:name=”android.permission.ACCESS_FINE_LOCATION” />
So now, you can use your custom module, try to run your project on the android device and see the result.

photo

Recent Blogs