title: Trouble running a background service in react native
body: <p>This is my very first post here, so please don't blame me if I'm not as complete and clear as I have to be.</p>
  
  <h1><strong>The issue</strong></h1>
  
  <p>I am new to React native and I recently began to develop a react native app which could read my incoming SMS's aloud. I already achieved to retrieve the incoming messages and to read them aloud... But only if the app is the foreground.</p>
  
  <p>So, could you please advise me some libraries or tutorials on the subject ?</p>
  
  <p>I'm working on a Nokia 5 with Android 9.</p>
  
  <p>I currently use the following libraries :</p>
  
  <ol>
  <li><a href="https://github.com/andreyvital/react-native-android-sms-listener" rel="nofollow noreferrer">React-native-android-sms-listener</a> to retrieve the incoming messages.</li>
  <li><a href="https://github.com/ak1394/react-native-tts" rel="nofollow noreferrer">React-native-tts</a> to read the content aloud.</li>
  </ol>
  
  <h1><strong>What I already tried</strong></h1>
  
  <p>I'm searching the Internet for more than a week now (includig Stack Overflow and <a href="https://stackoverflow.com/questions/35376690/how-can-i-run-background-tasks-in-react-native">this example question</a>) and I can't find what I'm looking for. I already tried <a href="https://github.com/ocetnik/react-native-background-timer" rel="nofollow noreferrer">React-native-background-timer</a> and <a href="https://github.com/vikeri/react-native-background-job" rel="nofollow noreferrer">React-native-background-job</a>. But I couldn't never get a background timer working and React-native-background-job allows tasks to be executed every 15 minutes only (due to the Android limitations).</p>
  
  <p>So I read many articles like <a href="https://medium.com/@essamtarik2000/introduction-to-react-natives-headless-js-90e87dee0ab4" rel="nofollow noreferrer">this one</a> explaining how to use <a href="https://facebook.github.io/react-native/docs/headless-js-android#docsNav" rel="nofollow noreferrer">Headless JS</a> and other libraries until I found <a href="https://codeburst.io/simple-android-call-recorder-in-react-native-headlessjs-task-614bcc56efc4" rel="nofollow noreferrer">this codeburst tutorial</a> today, explaining how to develop a background service to record audio calls. I tried to adapt it, but the background service never starts.</p>
  
  <h1>My code</h1>
  
  <p>I must tell you that I don't have any knowledge in Java, so the native code below may contain mistakes, even if it is based on tutorials and the React native documentation.</p>
  
  <p>Currently, when the app is launched, the service <em>IncomingSMSService</em> is called. This service, developed following the Codeburst tutorial referenced above, relies on Headless JS and a JS function that listen to the incoming messages and then read them aloud thanks to React-native-tts.</p>
  
  <p>Here is these two files :</p>
  
  <p><em>IncomingSMSService.java</em></p>
  
  <pre><code>package com.ava.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;
  
  public class IncomingSMSService extends HeadlessJsTaskService {
    @Override
    protected HeadlessJsTaskConfig getTaskConfig(Intent intent) {
      Bundle extras = intent.getExtras();
  
      if (extras != null) {
      return new HeadlessJsTaskConfig(
        "HandleIncomingSMS",
        Arguments.fromBundle(extras),
        5000,
        true
        );
      }
      return null;
    }
  }
  </code></pre>
  
  <p><em>HandleIncomingSMS.js</em></p>
  
  <pre><code>import { AppRegistry } from 'react-native';
  import SmsListener  from 'react-native-android-sms-listener';
  import Tts from 'react-native-tts';
  
  const HandleIncomingSMS = async (taskData) =&gt; {
    SmsListener.addListener(message =&gt; {
      Tts.getInitStatus().then(() =&gt; {
        Tts.speak(`New message from number ${message.originatingAddress} : ${message.body}`);
      });
    });
  }
  
  AppRegistry.registerHeadlessTask('HandleIncomingSMS', () =&gt; HandleIncomingSMS));
  </code></pre>
  
  <p>These pieces of code are called in a BroadcastReceiver here (<em>IncomingSMSReceiver.java</em>) :</p>
  
  <pre><code>package com.ava.receiver;
  
  import android.app.ActivityManager;
  import android.app.ActivityManager.RunningAppProcessInfo;
  import android.content.BroadcastReceiver;
  import android.content.Context;
  import android.content.Intent;
  import com.ava.service.IncomingSMSService;
  import com.facebook.react.HeadlessJsTaskService;
  import java.util.List;
  
  public final class IncomingSMSReceiver extends BroadcastReceiver {
    @Override
    public final void onReceive(Context context, Intent intent) {
      if (!isAppOnForeground((context))) {
        Intent service = new Intent(context, IncomingSMSService.class);
        context.startService(service);
        HeadlessJsTaskService.acquireWakeLockNow(context);
      }
    }
  
    private boolean isAppOnForeground(Context context) {
      ActivityManager activityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
      List&lt;ActivityManager.RunningAppProcessInfo&gt; appProcesses =
      activityManager.getRunningAppProcesses();
      if (appProcesses == null) {
          return false;
      }
      final String packageName = context.getPackageName();
      for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
          if (appProcess.importance ==
          ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND &amp;&amp;
           appProcess.processName.equals(packageName)) {
              return true;
          }
      }
      return false;
    }
  }
  </code></pre>
  
  <p>I also requested the good permissions in my AndroidManifest file, and I registered the service like so :</p>
  
  <pre><code>   &lt;service
      android:name="com.ava.service.IncomingSMSService"
      android:enabled="true"
      android:label="IncomingSMSService"
    /&gt;
    &lt;receiver android:name="com.ava.receiver.IncomingSMSReceiver"&gt;
      &lt;intent-filter android:priority="0"&gt;
        &lt;action android:name="android.provider.Telephony.SMS_RECEIVED"/&gt;
      &lt;/intent-filter&gt;
    &lt;/receiver&gt;
  </code></pre>
  
  <p>What am I doing wrong ? I don't even see service in the <em>Running services</em> tab of the Android Developer options... Any ideas ?</p>
  
  <p>Thanks in advance for your help.</p>
  
  <h1>UPDATE (01/06/2019)</h1>
  
  <p>After reading or watching several tutorials like <a href="https://dev.to/mathias5r/how-to-create-an-unstoppable-service-in-react-native-using-headless-js-android-5f61" rel="nofollow noreferrer">this one</a> or <a href="https://youtu.be/FbpD5RZtbCc" rel="nofollow noreferrer">this video</a>, I managed to get my app working in the foreground. It now displays a persistent notification.</p>
  
  <p>BUT, I don't know how I can "link" my service and my Broadcsat Receiver to this notification (for now, the service is called only if the app is in foreground).</p>
  
  <p>Here is my updated code :</p>
  
  <pre class="lang-java prettyprint-override"><code>// IncomingSMSService
  
  package com.ava.service;
  
  import android.graphics.Color;
  import android.app.Notification;
  import android.app.PendingIntent;
  import android.app.Service;
  import android.content.Context;
  import android.content.ContextWrapper;
  import android.app.Notification;
  import android.app.NotificationChannel;
  import android.app.NotificationManager;
  import android.content.Intent;
  import android.os.Handler;
  import android.os.IBinder;
  import android.support.v4.app.NotificationCompat;
  
  import com.facebook.react.HeadlessJsTaskService;
  import com.ava.MainActivity;
  import com.ava.R;
  
  public class IncomingSMSService extends Service {
      private NotificationManager notifManager;
      private String CHANNEL_ID = "47";
      private int SERVICE_NOTIFICATION_ID = 47;
  
      private Handler handler = new Handler();
      private Runnable runnableCode = new Runnable() {
          @Override
          public void run() {
              Context context = getApplicationContext();
              Intent myIntent = new Intent(context, IncomingSMSEventService.class);
              context.startService(myIntent);
              HeadlessJsTaskService.acquireWakeLockNow(context);
              handler.postDelayed(this, 2000);
          }
      };
  
  
      @Override
      public IBinder onBind(Intent intent) {
          return null;
      }
  
      @Override
      public void onCreate() {
          super.onCreate();
          createNotificationChannel();
      }
  
      @Override
      public void onDestroy() {
          super.onDestroy();
      }
  
      public void createNotificationChannel() {
        NotificationChannel notificationChannel = new NotificationChannel(CHANNEL_ID, "General", notifManager.IMPORTANCE_HIGH);
        notificationChannel.enableLights(true);
        notificationChannel.setLightColor(Color.RED);
        notificationChannel.setShowBadge(true);
        notificationChannel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
        getManager().createNotificationChannel(notificationChannel);
      }
  
      @Override
      public int onStartCommand(Intent intent, int flags, int startId) {
          this.handler.post(this.runnableCode);
          Intent notificationIntent = new Intent(this, MainActivity.class);
          PendingIntent contentIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
          Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                  .setContentTitle("Ava")
                  .setContentText("Listening for new messages...")
                  .setSmallIcon(R.mipmap.ic_launcher_round)
                  .setContentIntent(contentIntent)
                  .setOngoing(true)
                  .build();
          startForeground(SERVICE_NOTIFICATION_ID, notification);
          return START_NOT_STICKY;
      }
  
      private NotificationManager getManager() {
         if (notifManager == null) {
            notifManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
         }
         return notifManager;
     }
  }
  </code></pre>
  
  <p>My headlessJS task :</p>
  
  <pre><code>// HandleIncomingSMS.js
  
  import SmsListener from 'react-native-android-sms-listener';
  import Tts from 'react-native-tts';
  import Contacts from 'react-native-contacts';
  import { text } from 'react-native-communications';
  
  module.exports = async () =&gt; {
    // To lower other applications' sounds
    Tts.setDucking(true);
  
    // Prevent the TTS engine from repeating messages multiple times
    Tts.addEventListener('tts-finish', (event) =&gt; Tts.stop());
  
    SmsListener.addListener(message =&gt; {
      Contacts.getAll((err, contacts) =&gt; {
        if (err) throw err;
  
        const contactsLoop = () =&gt; {
          contacts.forEach((contact, index, contacts) =&gt; {
            // Search only for mobile numbers
            if (contact.phoneNumbers[0].label === 'mobile') {
              // Format the contact number to be compared with the message.oritignatingAddress variable
              let contactNumber = contact.phoneNumbers[0].number.replace(/^00/, '+');
              contactNumber = contactNumber.replace(/[\s-]/g, '');
              // Phone numbers comparison
              if (contactNumber === message.originatingAddress) {
                if (contact.familyName !== null) {
                  Tts.speak(`Nouveau message de ${contact.givenName} ${contact.familyName} : ${message.body}`);
                } else {
                  // If the contact doesn't have a known family name, just say his first name
                  Tts.speak(`Nouveau message de ${contact.givenName} : ${message.body}`);
                }
              } else if (contactNumber !== message.originatingAddress &amp;&amp; index === contacts.length) {
                // If the number isn't recognized and if the contacts have been all checked, just say the phone number
                Tts.speak(`Nouveau message du numéro ${message.originatingAddress} : ${message.body}`);
              }
            }
          });
        }
        contactsLoop();
        // Redirect to the SMS app
        text(message.originatingAddress, message = false);
      });
    });
  }
  </code></pre>
  
  <p>I also added the good permissions in my AndroidManifest.xml file like the following :</p>
  
  <pre class="lang-xml prettyprint-override"><code>...
  &lt;uses-permission android:name="android.permission.FOREGROUND_SERVICE"/&gt;
  &lt;uses-permission android:name="android.permission.WAKE_LOCK"/&gt;
  ...
  </code></pre>
  
  <p>I made some progress but I am still stuck, so if you have any idea, please share them ! Thank you !</p>