How to create an unstoppable service in React Native using Headless JS

Mathias Silva
React Brasil
8 min readMay 22, 2019

--

Unstoppable

If you have already developed a mobile application with a relative complexity, probably you found yourself breaking your head out with some unusual tasks. In most cases, there aren't enough resources to help you and, in this situation, you need to create your own resource. This post is an example of it.

I came across this situation developing one of my React Native applications where I needed to integrate events of a specific hardware. The challenge was that every event should be processed by the app regardless of the state of it, in other words, with the app in foreground, background, closed or even when it was not started (device reboots).

In this case, it’s necessary to have something not attached to the application but at the same time it must be unstoppable, alive almost one hundred percent of the time to execute the tasks. In a native android application, it can be done with the use of a component called Service which is able to execute tasks without an UI thread or a user action.

The main goal of this post is to explain how to implement a native android module to execute this kind of task in a React Native (RN) application, with the use of services, broadcast receivers and headless JS. In order to show how it works, I choose to demonstrate a simple heartbeat application which is described step by step in the following sections.

Step 1 — Creating the Bridge

Bridging

For a full understanding, I will present a tiny brief of how to implement a bridge to a native module in RN. If you like automatic process or want an easier way to do this I recommend using react-native-create-bridge or if you very familiar with this step, you can skip right to step 2.

In the actual context, a bridge is a means of communication between the RN layer and the native layer. Just as the documentation says, to create a bridge, firstly you must provide a Package responsible to register your module or UI module (not used here) in the RN layer. As shown in the class below, it’s instantiated the module passing the react context as a parameter.

The Module itself is responsible for defining the methods and props that will be available to the RN layer in the native layer. To expose a Java method, it must be annotated using @ReactMethod and the return will be always void. The bridge is asynchronous, so the only way to pass a result to RN layer is by using callbacks or emitting events.

To finish the bridge, it’s just needed to connect it to the two sides. In the MainApplication.java in the android folder, automatically created with the RN project, you must instantiate the new module in the getPackages method. Once you have done so, the methods and props of the module will be available and you can import and use then like below.


// Heartbeat.js
import { NativeModules } from ‘react-native’;const { Heartbeat } = NativeModules;export default Heartbeat;

Important: The module’s name must be the same defined by the REACT_CLASS variable in module class.

Step 2 — Creating the Service

As commented before, a service is used to execute tasks detached from the main thread of the application. In this example, the created service is responsible, when it’s started, to send heartbeat events in a specific interval of time. There are some ways to implement an interval in android, but here I used a Handler to process a runnable object and send the event every 2 seconds. The RCTDeviceEventEmitter can signal events to RN layer and it can be obtained directly from the react context, like below.

The documentation of Android Services is very clear about how would be the impact of the android’s system manager on your service. If it finds out that your device is in critical operation like low memory or low battery, it will kill some services to free these resources. To prevent this from happening, you have to turn your service to a Foreground kind and to do so a notification must be fixed on the status bar of the device. With this, the notification were included in the onStartMethod of the service class, like below.

Important: For the service to work, it’s also necessary to declare it in the AndrodManifest.xml. See this.

For API 26+, it was included a new way to create the notification channel, demonstrated in the snippet bellow.

Step 3 — Creating the BroadcastReceiver

A BroadcastReceiver, as the name itself says, it’s a responsive component to broadcast messages from the android system or from another apps, similar to the publish-subscribe design pattern. There are many broadcast messages available in an android system, but in our case the most important one is the BOOT_COMPLETED. It tells us that that the device suffered a reboot operation and this process is finished.

Listening to this specific message, our broadcast receiver will be able to restart the service created before some seconds after the device reboot process finishes, which will keep sending the heartbeat events. The implemented code is shown below.


// BootUpReceiver.java
public class BootUpReceiver extends BroadcastReceiver {@Override
public void onReceive(Context context, Intent intent) {
context.startService(new Intent(context, HeartbeatService.class));
}
}

In the AndrodManifest.xml, it’s also needed to declare the broadcast receiver and define the message that it will be listening. The corresponding permissions to read this kind of message must be provided as well.

Step 4 — Creating the Application

Certainly, this is the simplest step of this tutorial but in the same time it’s the most exciting one. It shows the result of everything done so far. In the RN layer, we need implement a component to interact and shows to the user the heartbeats events and I thought that the most suitable example to this event will be a heart pulse action, so let’s do it.

Heartbeat

In the App.js of the RN project I created a simple component as you can see below. With the benefits provided by the useState and useEffect of React Hooks, a listener is registered and every heartbeat event will change the width and height of a heart image. Simple enough, right? Moreover, the press of a button component calls the startService method from the native layer.

Finally, this is the time to test our application. As commented in the beginning of this article, the app to be approved in our test, it must to process the events in its 4 states (foreground, background, closed or not started). In the first state, when the app is shown to the user, it performs like the gif below. When the service is started a notification is pop up in the status bar of the device.

Probably, you are wondering about how we could see the processing of the event in the other states of the app. That’s when we run the command react-native log-android in the root folder of the project. Running this in a command line we can se the console.log executed by the service when the app is in background or closed, like below.

Ok, now we see that the application in doing well, but the test when the app is not started is missing. We expect that the broadcast receiver will start the service again and it will continue generating the heartbeat events to the RN layer and we can see the console.log messages again. But for our surprise it does not happen. Frustrating right?

When the device reboots and the broadcast message is sent, the service created in the the native layer gets up and start to send the events. However, there is no action to also gets up the RN layer to receive these events. In this moment we have a NullPointerException when the service tries to access a nonexistent React Context. To solve this problem it’s time to use a great tool provided by the React API: Headless JS.

Step 5 — Creating the Headless Service

A Headless JS is a RN service able to encapsulate some javascript operations and execute them detached from the application, like an android service. The headless service can do any kind of operation, except UI operations. In the actual example, the headless service can substitute the RCTDeviceEventEmitter, removing the direct use of React Context in the native layer.

Firstly, in the HeartbeatService class, we replace the event emitter with the headless service start, like below. When we do it, the service will execute the operation and it will go into a “paused” mode, until it is started again after two seconds.

Now we create the HeartbeatEventService like below. You can notice that we don’t use the React Context, so the service has its own context. The name of the event in the settings must be the same defined before.

A headless service must be registered on AppRegistry and just as its documentation says, every app registry should be required early in the require sequence to make sure the JS execution environment is setup before other modules are required. In the index.js we call the registry like below.


// index.js
AppRegistry.registerHeadlessTask(‘Heartbeat’, () => MyHeadlessTask);

Now, the MyHeadlessTask is responsible to change the width and height of the heart image through a state. As we have here a centralized state shared with the App component, I decided to use Redux instead of hooks. With this, it’s just necessary to create the basic structure of redux (action, reducer and store) to set properly the state. The MyHeadlessTask method is shown below.

When a heartbeat is fired, the state passed to the App changes, rerendenring the whole component. If we test again with the reboot process we can see that even in this state the service is able to gets up and fires the event again. Oh yeah! That’s how it’s done.

Conclusion

We have done so far an unstoppable service in React Native integrating a native android module and using some important tools like broadcast receivers, headless JS and much more. For future works, the challenge is to do the same for an iOS system that has some restrictions about to do tasks with the app closed or not started.

If you want to see more of this content buy me a coffee:

All the code of this example you can see in mathias5r/rn-heartbeat

--

--