expo: Expo keeps stopping when the app is killed while background location methods

šŸ› Bug Report

Summary of Issue

I registered a task for receiving the location updates in the background using Location.startLocationUpdatesAsync(taskName, options), And defined a task in the global scope using TaskManager.defineTask(taskName, task). The app frequently crashes when killed if the background task is active.

  • The Expo client refuses to start unless clearing its storage on Android.

  • The standalone app starts after at least two trials with a crash message.

Environment

  • expo: ^38.0.0
  • expo-task-manager: ~8.3.0
  • expo-location: ~8.2.1

Screenshot

Steps to Reproduce

1- Location.startLocationUpdatesAsync 2- TaskManager.defineTask 3- Kill the application.

Expected Behavior vs Actual Behavior

  • Expected behavior: The running task terminates smoothly.
  • Actual behavior:: The running task is killed, and the app can be restarted without a crash message.

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 10
  • Comments: 40 (3 by maintainers)

Most upvoted comments

I have tested the background location methods with Expo SDK 39, and I still get the same behavior as before, i.e. the app crashes if it is ā€œswipedā€ away while the background location task is running. I’ve tested it on emulators with Android 9, 10 & 11, as well as on a real device with Android 11.

I’m really disappointed, as I desperately need this feature to work and had put all my hopes in the new version solving these issues (as described in the CHANGELOG for expo-location / expo-task-manager).

No problem. Something like this is what i have.

import {useEffect, useRef} from "react";
import haversine from "haversine";

  // In android, sdk 38 managed workflow, we cannot use startLocationUpdatesAsync.
  // So we add a callback in a component to check for changes in the location and send them to the server....
  // works "ok", but will not work when the app is shutoff. We'll have to wait until SDK 39.0 for it to hopefully be solved.
  // https://forums.expo.io/t/startlocationupdatesasync-crashes-app-on-sdk-38-android-managed/42035
  useLocationUpdateForAndroid = () => {
    const lastPosition = useRef<ICoordinates>();
    const locationEnabled = useRef<Boolean>(false);

    // We should not be running useLocationUpdateForAndroid in a place where we don't have permission async, but "just in case", I put this
    // check here.
    useEffect(() => {
      Location.getPermissionsAsync().then(({status}) => {
        if (status === "granted") {
          locationEnabled.current = true;
        }
      });
    }, []);

    const runLocationUpdate = () => locationEnabled.current ? Location.getLastKnownPositionAsync().then(({coords: {latitude, longitude}}) => {
      let updateLocation = true;
      // If they haven't moved more than 15m from last reading don't persist their new location.
      if (lastPosition.current && !haversine({latitude, longitude}, lastPosition.current, {unit: "meter", threshold: 15})) {
        updateLocation = false;
      }
      if (updateLocation) {
        // send to server
      }
      lastPosition.current = {latitude, longitude};
    }) : undefined;

    useEffect(() => {
      const interval = setInterval(() => runLocationUpdate(), 30000);
      return () => clearInterval(interval);
    }, []);
  };

Then you run this in your main app navigator when the user is logged into your app. If they are signed out this component will not render and will not try to send background info to the server.

I’d consider this a serious issue. The background location feature is not working on Android and crashes the dev client and standalone apps.

@michaelkleinhenz just for clarity, and understanding for everyone…

When you said you tried different versions of the dependency, I’m assuming you didn’t actually eject the project? If that’s the case it makes sense that it still ā€œDoesn’t Workā€ on Expo 38, because the bug is in native code.

In order to test that those versions did fix the issue, you would need to be testing with an ejected project in the bare flow.

If you tested it with an bare/ejected project, then we should address that by looping in the person who fixed it originally, but I don’t believe that is the case.

These definitely seem like the same issue. @m-jachym they can fix it, and I think they have… but the issue is in native code, not JavaScript. In order to fix the issue they need to compile a new root binary for the managed workflow, which I’m sure is no small task. They have to update their CI to run the new release, development apps, along with many other things. If you are concerned about needing this fix sooner, you can always eject, to the bare workflow. There are pros and cons to each flow, and the managed one is that you are relying on Expo to do all the complicated things you don’t want to do.

I am still not convinced that it is actually fixed in 39. I haven’t seen a PR that directly refers to this issue. And you’re right, this is a major issue in an essential component. This should be fixed asap and with a hotfix. It is clearly understandable and clearly verifyable. And given that there are mutiple issues related to this issue, has a bit of gravity to it.

My fear is that this is an issue that exposes some deeper issues that are not easily fixable. I’ve seen a few source comments like ā€œmore checks for NPEs, if this does not help, we have a deeper issue with thread safetyā€¦ā€. Those stuff makes me concerned…

I am also experiencing this issue and encountering the, ā€œCannot initialize app loader. host.exp.exponent.taskManager.ExpoHeadlessAppLoaderā€ error. This occurs on an Android simulator when testing using Expo and using a standalone app published to the Google Play store.

Could someone confirm that this error does not occur prior to SDK 38 before I try downgrading?

Edit: no luck with SDK 37 😦

Edit 2: I got it working after ejecting. I’m gonna miss expo publish 😭

Ok, but this solution only works if the app is in the foreground and used. That will not work for me, sadly, as I need a true background task to fech location and notify the user if some location matches.

In the meantime…I created my own ā€œbackground taskā€ for location on android only. It will poll the users location and send it up while the app is not killed… It will tie me over until the 39 release.

@ankit-kumar615 - this is not related to this issue. please create a new issue with more info if you believe this is a bug in expo code.

A project which was working fine a couple of days ago (sdk 26.0.0) has stopped working now. When trying to open it, the app crashes with only the ā€œExpo keeps stoppingā€ error message. I proceeded to update the cli, the sdk and the android app to their latest versions, however, even now when i run a project with expo init and expo start, the app simply crashes. The apps in the Featured section of the expo android app seem to be working fine. Does anybody have any experience with this? This has pretty much killed all the expo projects i am working on and any insight would be greatly appreciated

@Obversity the linked ticket states it will go out in 39

I am encountering possibly the same issue (similar stacktrace), but I am not making any use of location services.

I get the following stacktrace upon startup of the app (only when built as standalone .apk):

2020-08-27 21:14:26.021 18702-18702/? E/Expo: Cannot initialize app loader. host.exp.exponent.taskManager.ExpoHeadlessAppLoader
2020-08-27 21:14:26.021 18702-18702/? W/System.err: java.lang.ClassNotFoundException: host.exp.exponent.taskManager.ExpoHeadlessAppLoader
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at java.lang.Class.classForName(Native Method)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at java.lang.Class.forName(Class.java:454)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at java.lang.Class.forName(Class.java:379)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at l.d.a.b.a(AppLoaderProvider.java:2)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at l.d.a.b.b(AppLoaderProvider.java:2)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at expo.modules.taskManager.TaskService.getAppLoader(TaskService.java:2)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at expo.modules.taskManager.TaskService.isStartedByHeadlessLoader(TaskService.java:1)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at expo.modules.taskManager.TaskManagerInternalModule.isRunningInHeadlessMode(TaskManagerInternalModule.java:1)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at expo.modules.taskManager.TaskManagerInternalModule.onHostResume(TaskManagerInternalModule.java:1)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at org.unimodules.adapters.react.services.d$c.onHostResume(UIManagerModuleWrapper.java:2)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at com.facebook.react.bridge.ReactContext.onHostResume(ReactContext.java:5)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at com.facebook.react.ReactInstanceManager.moveToResumedLifecycleState(ReactInstanceManager.java:3)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at com.facebook.react.ReactInstanceManager.onHostResume(ReactInstanceManager.java:11)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at com.facebook.react.ReactInstanceManager.onHostResume(ReactInstanceManager.java:3)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at host.exp.exponent.k.a(RNObject.java:17)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at host.exp.exponent.k.a(RNObject.java:12)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at host.exp.exponent.k.a(RNObject.java:58)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at host.exp.exponent.experience.k.onResume(ReactNativeActivity.java:3)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at host.exp.exponent.experience.f.onResume(BaseExperienceActivity.java:1)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at host.exp.exponent.experience.ExperienceActivity.onResume(ExperienceActivity.java:1)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.Instrumentation.callActivityOnResume(Instrumentation.java:1453)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.Activity.performResume(Activity.java:7962)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4195)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4237)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.os.Handler.dispatchMessage(Handler.java:107)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.os.Looper.loop(Looper.java:214)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at android.app.ActivityThread.main(ActivityThread.java:7356)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at java.lang.reflect.Method.invoke(Native Method)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
2020-08-27 21:14:26.021 18702-18702/? W/System.err: Caused by: java.lang.ClassNotFoundException: host.exp.exponent.taskManager.ExpoHeadlessAppLoader
2020-08-27 21:14:26.021 18702-18702/? W/System.err: … 35 more

@Kokopelli84 @Aryk Is there a difference between ejecting and manually updating location-manager to 8.4.0 in package.json? Because I tried the package.json variant and also verified the deps had 8.4.0 in node_modules, but the client was still crashing…

It doesn’t work that way. The reason is that it is not supported by Expo SDK 38. Have a look here to see an explanation for it.

Also tried to upgrade expo-taskmanager to 8.4.0, no luck, same crash.