TaskManager crashes on Android


#1

I am not sure if the topic is related to detached apps only but I am running one so here it goes. I am working on an application that uses location updates in the background. While testing the feature on Android I found that a crash happens after several minutes in the background.

Test Device: Pixel 3XL
OS Version: Android 9 (Pie)

Stacktrace:

02-26 17:02:11.431 15639 15639 E AndroidRuntime: FATAL EXCEPTION: main
02-26 17:02:11.431 15639 15639 E AndroidRuntime: Process: [...], PID: 15639
02-26 17:02:11.431 15639 15639 E AndroidRuntime: java.lang.RuntimeException: Unable to start receiver expo.modules.taskManager.TaskBroadcastReceiver: java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.ActivityThread.handleReceiver(ActivityThread.java:3426)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.ActivityThread.access$1200(ActivityThread.java:200)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1667)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:106)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.os.Looper.loop(Looper.java:193)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:6718)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
02-26 17:02:11.431 15639 15639 E AndroidRuntime: Caused by: java.lang.IllegalStateException: Apps may not schedule more than 100 distinct jobs
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.os.Parcel.createException(Parcel.java:1958)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.os.Parcel.readException(Parcel.java:1918)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.os.Parcel.readException(Parcel.java:1868)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.job.IJobScheduler$Stub$Proxy.schedule(IJobScheduler.java:184)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.JobSchedulerImpl.schedule(JobSchedulerImpl.java:44)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at expo.modules.taskManager.TaskManagerUtils.scheduleJob(TaskManagerUtils.java:59)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at expo.modules.taskManager.TaskManagerUtils.scheduleJob(TaskManagerUtils.java:71)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at expo.modules.location.taskConsumers.LocationTaskConsumer.didReceiveBroadcast(LocationTaskConsumer.java:104)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at expo.modules.taskManager.TaskService.handleIntent(TaskService.java:283)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at expo.modules.taskManager.TaskBroadcastReceiver.onReceive(TaskBroadcastReceiver.java:15)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.ActivityThread.handleReceiver(ActivityThread.java:3417)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     ... 8 more
02-26 17:02:11.431 15639 15639 E AndroidRuntime: Caused by: android.os.RemoteException: Remote stack trace:
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at com.android.server.job.JobSchedulerService.scheduleAsPackage(JobSchedulerService.java:871)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at com.android.server.job.JobSchedulerService$JobSchedulerStub.schedule(JobSchedulerService.java:2568)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.app.job.IJobScheduler$Stub.onTransact(IJobScheduler.java:60)
02-26 17:02:11.431 15639 15639 E AndroidRuntime:     at android.os.Binder.execTransact(Binder.java:731)
02-26 17:02:11.431 15639 15639 E AndroidRuntime: 

Config options:

// https://docs.expo.io/versions/v32.0.0/sdk/location/
export const OPTIONS = {
  accuracy: Location.Accuracy.Highest, // This consumes a lot more power
  timeInterval: 500, // Minimum time to wait between each update in milliseconds.
  distanceInterval: 1, // Updates only when location has changed by at least this distance in meters.
  showsBackgroundLocationIndicator: false, // Hides blue bar on top
};

The application utilises redux-saga for handling location stuff. The location saga is started with the JS app and looks like this:

const locationSagas = function* locationSagas(): Saga<any> {
  while (true) {
    try {
      // Stop location updates in case of launching the app after killing it during the last workout
      Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME);

      // wait for observation initialization action
      const { type: startType } = startObservingLocationAction();
      yield put(logger('INFO', `Waiting for ${startType} request`));
      yield take(startType);

      const isTaskRegistered = yield !TaskManager.isTaskRegisteredAsync(LOCATION_TASK_NAME);
      if (isTaskRegistered) {
        throw new Error('Location task is not registered');
      }

      store.dispatch(logger('INFO', 'Start location updates'));
      Location.startLocationUpdatesAsync(LOCATION_TASK_NAME, OPTIONS);
      yield put(logger('INFO', 'Observation of location data started'));

      // wait for cancellation action
      const { type: stopType } = stopObservingLocationAction();
      yield put(logger('INFO', `Waiting for ${stopType} request`));
      yield take(stopType);
    } catch (e) {
      yield put(logger('ERROR', e.message, 'sagas/location#observeLocationData'));
    } finally {
      yield put(logger('INFO', 'Stop location updates'));
      Location.stopLocationUpdatesAsync(LOCATION_TASK_NAME);
    }
  }
};

I am not sure if the stopLocationUpdatesAsync at the beginning of the saga (which I suppose may be restarted if the TaskManager restarts the app) does mess up with the JobScheduler or not.


#2

Hey, how did you manage to run background tasks on android 8.0 + ? I did try it myself and after the app went to the background taskManager didn’t work. Do you know any workaround?


#3

I have just been testing and it seemed to work fine on Android 9.0 until the 100 jobs exception happened. Unless I have implemented something wrong I think there is a bug in the TaskManager implementation.

It is important that I register the task when the app is in foreground.