Introduction to React Native’s Headless JS
In this article I’m going to give you a simple introduction to Headless JS, which is React Native’s way of creating background tasks
Headless JS is a way to run tasks in JavaScript while your app is in the background. It can be used, for example, to sync fresh data, handle push notifications, or play music.
What we’re going to build is a simple app that detects changes in the device’s location and logs them (Headless JS is available for android only as for the time of this article)
If you’re anything like me, when i first tried Headless JS I was kind of puzzled
since I’m not very familiar with Native Android development
The official documentation only helped me with the theory but it wasn’t very detailed when it came to where to put those pieces of code
And so i had to spend quite some time learning about Android services and how to call them, So I hope this tutorial helps
Preparation
First, create a new project
react-native init LocationListener
There won’t be any need for any additional libraries, so we can proceed to the next step
Grant Permissions
Since we need to find the device’s current location we have to add the proper permissions
open LocationListener/android/app/src/main/AndroidManifest.xml
Add the following permissions :
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
Creating our service
Headless JS is simply a bridge between your Java service and your JS code, allowing the background Service to call a JS function of your definition
To create an Android service :
1- Add the following line to your AndroidManifest.xml, within your <application> tags:
<service android:name="com.locationlistener.service.LocationService" />
2- Create the service file in :
LocationListener/android/app/src/main/java/com/locationlistener/service/LocationService.java
3- Add the following code in the service file
//imports
package com.locationlistener.service;
import android.content.Intent;
import android.os.Bundle;
import com.facebook.react.HeadlessJsTaskService;
import com.facebook.react.bridge.Arguments;
import com.facebook.react.jstasks.HeadlessJsTaskConfig;
import javax.annotation.Nullable;//creating the service class
public class LocationService extends HeadlessJsTaskService {
@Nullable
protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
Bundle extras = intent.getExtras();
return new HeadlessJsTaskConfig(
"LogLocation", //JS function to call
extras != null ? Arguments.fromBundle(extras) : null,
5000,
true);
}
}
The line that matters most to us in this file is the first argument passed to HeadlessJsTaskConfig, which is the name of the JS function to call from the service, “LogLocation” in our case
Let’s create that function
4- Create the JS function
In the top directory add the following function to your App.js outside of any classes:
import {
AppRegistry
} from 'react-native';const LogLocation = async (data) => {
navigator.geolocation.getCurrentPosition((position) => {
console.log(position.coords);
});
}AppRegistry.registerHeadlessTask('LogLocation', () => LogLocation);
This function will get the current position from React Native’s navigator and log it to the console
then the the last line of code registers it as a Headless task with the name LogLocation, so it can be called from our previously created service
Calling our service
Now that we have created the service and assigned it to a javascript function
we need to tell the App when this service should be called
In our case we need this service to be called whenever a change in the device’s location is detected
For this purpose we can use Android’s LocationListener class and register our service as a callback for each change in location
open MainApplication.java:
LocationListener/android/app/src/main/java/com/locationlistener/MainApplication.java
First, import the following:
import android.location.LocationManager;
import android.location.LocationListener;
import android.location.Location;
import com.locationlistener.service.LocationService;
import android.content.Context;
import android.os.Bundle;
import android.content.Intent;
import com.facebook.react.HeadlessJsTaskService;
Then create an instance of the LocationListener and override its onLocationChanged function:
private final LocationListener listener = new LocationListener() {
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
@Override
public void onProviderEnabled(String provider) {
}@Override
public void onProviderDisabled(String provider) {
}@Override
public void onLocationChanged(Location location) {
Intent myIntent = new Intent(getApplicationContext(), LocationService.class);
getApplicationContext().startService(myIntent); HeadlessJsTaskService.acquireWakeLockNow(getApplicationContext());
}
};
Now register to this listener in the class’s onCreate function :
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Start requesting for location
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 1, listener);
onCreate should look like this now:
@Override
public void onCreate() {
super.onCreate();
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
// Start requesting for location
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 2000, 1, listener);
SoLoader.init(this, /* native exopackage */ false);
}
Now run
react-native run-android
and turn on remote js debugging and you’ll see how your app will log each change in location
All the code for this article can be found here:
Thanks for reading.