Introduction to React Native’s Headless JS

essam tarik
3 min readFeb 16, 2018

--

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

--

--

essam tarik

I’m a Full-Stack engineer, a linux enthusiast and a dreamer :)