Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

locationListener doesn't work after 30 seconds on foreground service

I have created a service which finds and then stores the user's coordinates in an SQLite database.

public class GPS_Service extends Service {

DatabaseHelper myDb;

private LocationListener locationListener;
private LocationManager locationManager;

private String latitude, longitude;

@Override
public void onCreate() {
    super.onCreate();

    myDb = new DatabaseHelper(this);

}

@SuppressLint("MissingPermission")
@Override
public int onStartCommand(Intent intent, int flags, int startId) {

    Intent notificationIntent = new Intent(this, MainActivity.class);
    PendingIntent pendingIntent = PendingIntent.getActivity(this,0, notificationIntent, 0);

    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setContentTitle("Service")
            .setContentText("Coordinates Location Running")
            .setContentIntent(pendingIntent)
            .build();

    startForeground(1, notification);

    locationListener = new LocationListener() {

        @Override
        public void onLocationChanged(Location location) {

            Log.d("myTag", "Hello");

            latitude = String.valueOf(location.getLatitude());
            longitude = String.valueOf(location.getLongitude());

            insertCoordinates(latitude, longitude);

            Intent i = new Intent("location_update");
            i.putExtra("latitude", latitude);
            i.putExtra("longitude",longitude);

            sendBroadcast(i);

        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {

        }

        @Override
        public void onProviderEnabled(String provider) {

        }

        @Override
        public void onProviderDisabled(String provider) {

            Intent i = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            startActivity(i);

        }

    };

    locationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 0, locationListener);

    return START_NOT_STICKY;

}

@Override
public void onDestroy() {

    super.onDestroy();

    if(locationManager != null)
        locationManager.removeUpdates(locationListener);

}

private void insertCoordinates(String latitude, String longitude) {

    boolean inserted = myDb.insertData(latitude, longitude); //Insert coordinates

    //Check if insertion is completed
    if(inserted)
        Toast.makeText(GPS_Service.this, "Coordinates Inserted", Toast.LENGTH_SHORT).show();
    else
        Toast.makeText(GPS_Service.this, "Coordinates Not Inserted", Toast.LENGTH_SHORT).show();

}

@Nullable
@Override
public IBinder onBind(Intent intent) {
    return null;
}

}

I can either start or stop the service from the MainActivity like this

private void enable_buttons() {

    buttonStartService.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Intent serviceIntent = new Intent(getApplicationContext(), GPS_Service.class);

            //Checks if the SDK version is higher than 26 to act accordingly
            ContextCompat.startForegroundService(MainActivity.this, serviceIntent);

        }
    });

    buttonStopService.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Intent serviceIntent = new Intent(MainActivity.this, GPS_Service.class);
            stopService(serviceIntent);

        }
    });

}

The problem is that when I start this service, if I either completely close the app or leave it in the background, the locationListener will work for 30 seconds and then it will stop. If I reopen the app, the service continues to work from where it stopped. Also I checked in the developer options if the service is running, and it indeed is even though the locationListener doesn't output the expected results. Any ideas?

like image 231
Iraklis Eleftheriadis Avatar asked Nov 07 '19 09:11

Iraklis Eleftheriadis


People also ask

How do you stop a foreground service?

The service must stop itself by calling stopSelf(), or another component can stop it by calling stopService(). Once requested to stop with stopSelf() or stopService(), the system destroys the service as soon as possible.

What is the difference between foreground and background services on Android?

A Background Service is a service that runs only when the app is running so it'll get terminated when the app is terminated. A Foreground Service is a service that stays alive even when the app is terminated. And a Bound Service is a service that runs only if the component it is bound to is still active.

What does foreground service mean?

A foreground service performs some operation that is noticeable to the user. For example, an audio app would use a foreground service to play an audio track. Foreground services must display a Notification. Foreground services continue running even when the user isn't interacting with the app.


1 Answers

TL;DR:

Add android:foregroundServiceType="location" to your Service's manifest entry.

EXPLANATION

This new behavior, for Android 10, is exactly as you've described: Even though you may be using a foreground service, 30 seconds after your app leaves the screen -- location updates cease.

You might've noticed that Android 10 devices present two new choices to the user when granting location permissions (for legacy (API < 29) apps, or apps that declare the ACCESS_BACKGROUND_LOCATION permission):

  • "Allow all the time"
  • "Allow only while using this app"

"Allow only while using this app" effectively means, "Allow while the app is visible onscreen". That's because the user now has the option of selectively removing location access -- even to a foreground service -- based on that criteria. Users can change this setting at any time, even if your app is running.

The Android docs explain that the solution, android:foregroundServiceType="location", was intended for your precise use case: "Google Maps"-like apps, which have a foreground service, but are expected to continue processing location data if the user switches to another app. The technique is called "continuing a user-initiated action", and it allows you to get location updates even after your app is placed in the "background".

(The docs seem to be expanding the definition of the term "background", here. In the past, if you had a foreground service, your app was considered "in the foreground" -- at least for the purposes of task priority, Doze, and so forth. Now it appears that an app is considered "in the background", with respect to location access, if it hasn't been onscreen in the last 30 seconds.)

I am not sure what UI changes (like in the Google Play store) take place when you set a particular foregroundServiceType. Regardless, it seems to me that users are unlikely to object.

OTHER SOLUTIONS FOR ANDROID 10 DEVICES

Alternatively, you could've declared a targetSdkVersion of 28 or less, which will let your app function in a location "compatibility mode".

You also have the option of gaining the ACCESS_BACKGROUND_LOCATION permission, but the docs caution against this:

If your app doesn't require location access while running in the background, it's highly recommended that you not request ACCESS_BACKGROUND_LOCATION...

This approach isn't required, for your use case, because your Activity is used to start your Service; you can be guaranteed that your app has been onscreen at least once before the Service starts getting background location updates. (At least, I assume that that is how the OS determines the start of a "user-initiated action". Presumably, the foregroundServiceType approach won't work if you're starting the Service from a JobScheduler, or a BroadcastReceiver, or something.)

PS: Hang on to that WakeLock code. You're going to need to keep the device awake, if you want to keep getting updates at a steady 10-second pace.

like image 52
greeble31 Avatar answered Oct 19 '22 03:10

greeble31