question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Task Manager Job Doesn't Get Fired: Location stops updating after 5-10 minutes on Android (when using Foreground Service)

See original GitHub issue

Summary

I’m testing Expo 42 with an ejected app (bare workflow). My app watches the current device location in foreground (when the app is open) and background (when the screen is turned off) using Android’s Foreground Service functionality, by permanently displaying a notification icon in the status bar.

Generally, it works as expected. However, after about 5 to 10 minutes, Android seems to put the device to “sleep” and that’s when my task (TaskManager.defineTask) doesn’t get fired periodically anymore. Instead, I only receive batch updates every couple of minutes, but not in real-time anymore, and thus useless for a real-time app relying on accurate location info. This is reproducible on both a real device as well as the Emulator running API 30.

I have spent about half a day debugging through Expo sources and finally found the culprit: At some point, when Android puts the device into a deeper sleep (aka “dozing”) after 5 minutes or so, the Expo Task Manager stops firing. Jobs are still scheduled but they aren’t run immediately anymore but batched and run only after a long delay of several minutes. I’m talking about this code in expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskManagerUtils.java:

      JobInfo jobInfo = createJobInfo(context, task, newJobId, data);
      jobScheduler.schedule(jobInfo);

The job will simply not fire right away and subsequent jobs will get merged without getting fired either. These deferred jobs will apparently only run every 10 to 15 minutes or so.

I finally got a workaround going by increasing the priority of the job by using setImportantWhileForeground. I don’t really know the implications for other parts of Expo or on the battery usage, but for background location, this seems to work as expected now. Here’s a patch for TaskManagerUtils.java that I’m applying in my local repo using patch-package:

diff --git a/node_modules/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskManagerUtils.java b/node_modules/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskManagerUtils.java
index b40f2d1..7d47fd7 100644
--- a/node_modules/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskManagerUtils.java
+++ b/node_modules/expo-task-manager/android/src/main/java/expo/modules/taskManager/TaskManagerUtils.java
@@ -8,6 +8,7 @@ import android.content.ComponentName;
 import android.content.Context;
 import android.content.Intent;
 import android.net.Uri;
+import android.os.Build;
 import android.os.Bundle;
 import android.os.Parcelable;
 import android.os.PersistableBundle;
@@ -195,11 +196,16 @@ public class TaskManagerUtils implements TaskManagerUtilsInterface {
   }
 
   private JobInfo createJobInfo(int jobId, ComponentName jobService, PersistableBundle extras) {
-    return new JobInfo.Builder(jobId, jobService)
-      .setExtras(extras)
-      .setMinimumLatency(0)
-      .setOverrideDeadline(DEFAULT_OVERRIDE_DEADLINE)
-      .build();
+    JobInfo.Builder jobBuilder = new JobInfo.Builder(jobId, jobService)
+            .setExtras(extras);
+    if (Build.VERSION.SDK_INT < 28) {
+      jobBuilder.setMinimumLatency(1)
+              .setOverrideDeadline(DEFAULT_OVERRIDE_DEADLINE);
+    } else {
+      // FIXME: This method was deprecated in API level 31. Use setExpedited(boolean) instead.
+      jobBuilder.setImportantWhileForeground(true);
+    }
+    return jobBuilder.build();
   }
 
   private JobInfo createJobInfo(Context context, TaskInterface task, int jobId, List<PersistableBundle> data) {

This change ensures that Android keeps firing the job on time even if the device is sleeping.

Setting this to true indicates that this job is important while the scheduling app is in the foreground or on the temporary whitelist for background restrictions. This means that the system will relax doze restrictions on this job during this time. Apps should use this flag only for short jobs that are essential for the app to function properly in the foreground. Note that once the scheduling app is no longer whitelisted from background restrictions and in the background, or the job failed due to unsatisfied constraints, this job should be expected to behave like other jobs without this flag.

I’m also setting setMinimumLatency(1) as I’ve read somewhere that 0 might not always work as expected on some older Android versions when used in combination with setOverrideDeadline(). I didn’t bother testing it though 🤷 . Also, note that setOverrideDeadline has no effect if the device is dozing. Its value is simply ignored. But in any case, a deadline of 1 minute wouldn’t really be sufficient for real-time location updates anyway.

tl;dr.: Seems that the Expo Task Manager will stop running jobs such as the location updates once a device goes sleeping. The above patch avoids this by setting setImportantWhileForeground and making sure tasks keep firing.

Managed or bare workflow? If you have ios/ or android/ directories in your project, the answer is bare!

bare

What platform(s) does this occur on?

Android

SDK Version (managed workflow only)

No response

Environment

  Expo CLI 4.10.0 environment info:
    System:
      OS: macOS 11.5.2
      Shell: 5.8 - /bin/zsh
    Binaries:
      Node: 14.16.1 - ~/.nvm/versions/node/v14.16.1/bin/node
      npm: 7.20.3 - ~/.nvm/versions/node/v14.16.1/bin/npm
      Watchman: 2021.06.07.00 - /usr/local/bin/watchman
    Managers:
      CocoaPods: 1.10.2 - /usr/local/bin/pod
    SDKs:
      iOS SDK:
        Platforms: iOS 14.5, DriverKit 20.4, macOS 11.3, tvOS 14.5, watchOS 7.4
      Android SDK:
        API Levels: 29, 30
        Build Tools: 29.0.2, 29.0.3, 30.0.2, 30.0.3
        System Images: android-30 | Google APIs Intel x86 Atom, android-30 | Google Play Intel x86 Atom
    IDEs:
      Android Studio: 2020.3 AI-203.7717.56.2031.7583922
      Xcode: 12.5.1/12E507 - /usr/bin/xcodebuild
    npmPackages:
      expo: ^42.0.3 => 42.0.3 
      react: 17.0.1 => 17.0.1 
      react-dom: 17.0.1 => 17.0.1 
      react-native: ^0.64.1 => 0.64.2 
    npmGlobalPackages:
      expo-cli: 4.10.0
    Expo Workflow: bare

Reproducible demo or steps to reproduce from a blank project

None

Issue Analytics

  • State:closed
  • Created 2 years ago
  • Reactions:5
  • Comments:12 (2 by maintainers)

github_iconTop GitHub Comments

2reactions
AlonWiezercommented, Sep 28, 2021

Hi, is there any solution or deadline for this problem to be fixed in the managed workflow? its really critical for our app and we really dont want to eject 😦

1reaction
hbelcommented, Feb 14, 2022

This is so sad. Is there any way I can be of help to get this fixed in the codebase (implementing, testing, although I would only partly know what I’m doing there), so we can use the functionality also in managed workflows, or at least in bare workflows without patching stuff by hand?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Foreground service stops giving location updates after 10 ...
But none of them worked. My service stops giving me location update after 10 minutes. I tried to put job scheduler also but...
Read more >
Background Location Limits | Android Developers
If your app is running in the background, the location system service computes a new location for your app only a few times...
Read more >
Inconsistent location updates in foreground service when in ...
I'm using a foreground service that requests a location update with high accuracy at some point. It works well when the phone is...
Read more >
Track Users Location in the Background with Android and ...
Using Xamarin.Forms and Essentials, we will see how to implement a foreground service on Android that tracks the users location even when ...
Read more >
Get location every minute while app is terminated
I am working on a project, in which we need to access location of user every time whatever app is in foreground, background...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found