Reputation: 93
Sorry this is a long question, but I'm hoping that one of you experts out there can help a novice who is going quietly mad!!
I have an Android app which uses a background service to get GPS fixes (using Google Play Services) every 20 seconds. It compares the latitude and longitude with those in a list, and if it finds a match, it sends a broadcast to a receiver that triggers a foreground activity to alert the user.
I use a background service because typically there are between 2 and 20 minutes between user alerts, and in between, there is no user interaction. The app uses a foreground activity for the user to choose the options he wants, but then closes all foreground activities leaving only the background activity running.
This worked well on Android 4.3, on my old device, but I am now updating it run on Android 8 (Oreo) (testing it on a Sony Xperia XZ1Compact). I have added
<uses-permission
android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/
to the manifest, and this caused the app to ask for permission when first run. My device settings then show the app as having a power saving exception allowed.
The code for the background service (sorry there's rather a lot of it but I've included it all in case its relevant!) is as follows
package com.barney.trackgps;
import android.Manifest;
import android.app.Service;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.os.IBinder;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.util.Log;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
public class ApiTrackService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
GoogleApiClient mLocationClient;
LocationRequest mLocationRequest = new LocationRequest();
public static final String ACTION_LOCATION_BROADCAST =
ApiTrackService.class.getName() + "LocationBroadcast";
public static final String EXTRA_LATITUDE = "extra_latitude";
public static final String EXTRA_LONGITUDE = "extra_longitude";
int interval=20; //time between fixes in seconds
@Override
public void onDestroy(){
super.onDestroy();
stopSelf();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.v("sTag","Got to apitrack");
mLocationClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest.setInterval(interval*1000);
mLocationRequest.setFastestInterval(1000);
int priority = LocationRequest.PRIORITY_HIGH_ACCURACY;
mLocationRequest.setPriority(priority);
mLocationClient.connect();
return START_STICKY;
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onConnected(Bundle dataBundle) {
if (ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_FINE_LOCATION) !=
PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(this,
Manifest.permission.ACCESS_COARSE_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
return;
}
LocationServices.FusedLocationApi.requestLocationUpdates(mLocationClient,
mLocationRequest, this);
}
@Override
public void onConnectionSuspended(int i) {
}
//to get the location change
@Override
public void onLocationChanged(Location location) {
boolean locFound=false;
Log.v("sTag","Got a fix");
/*does stuff to compare latitude and longitude with places in a list
and sets locFound=true if it finds a match*/
if (location != null) {
GPSLog=GPSLog+"Found";
if (locFound) {
Log.v("sTag", "Sending broadcast");
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);
intent.setAction("com.AboutMyJourney.posBroadcast");
sendBroadcast(intent);
}
}
}
@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
//Log.d(TAG, "Failed to connect to Google API");
}
}
The app works fine as long as it has one of its foreground activities open and visible. Tracking using logcat shows that background service is getting GPS fixes and the code under onLocationChanged (see code above) is being run every 20 seconds.
The app still works fine, and the onLocationChanged method still gets run every 20 seconds, if I allow my app to close all its foreground activities and run only the background service above, as long as another (completely unrelated) app that is calling for GPS locations is open and visible.
If, however, I don't have any app that uses GPS visible on the screen, then the GPS tracking stops; the onLocationChanged Method is no longer run and I no longer get GPS fixes.
If I then open one of my app's foreground activities, GPS tracking starts up again, showing that the background service has not been killed.
I am assuming this is something to do with the changes that have been made to implement power saving/DOZE mode, and that I just don't understand it properly. However, all the advice I can find seems to suggest that the background service will go on working as long as it is allowed as an exception in the device's whitelist, but this clearly isn't happening.
It would be my preferred option to make the background service work, as that means the least work to write new code!. However, some responses to similar questions have suggested using a foreground service with a notification instead. Would that meet my requirement to have no User Interface visible (except maybe for a notification) most of the time, only prompting the user to do something every few minutes but leaving him or her free to do other stuff in the meantime? Would it work OK in older versions of Android as well?
Can anyone who understands this better than I do (it's probably not hard to understand it better than I do!) help, please?
Upvotes: 2
Views: 3232
Reputation: 966
starting from Android marshmallow google introduce a new battery optimization ways like
you can learn more about them through Doze and Stand by Mode
and this
in those concepts keep in contentious improvement till android Oreo. when google announce android Oreo they are heavily rely on battery optimization ways they had introduced new Apis to handle background operation Like work Manager And job scheduler
But they also enter in doze and stand by mode. and work in doze mode maintenance window.
after a long search and practicing the best way
To Keep tracking User Location Every 20 Second You need to Start A foreground Service With A sticky Foreground Notification.this way does not terminated by doze or stand by mode and it keep the Location Updated come according the interval.
here is explanation for Service and also an example for starting a foreground service
Upvotes: 1
Reputation: 5949
The applicable standby/doze documentation says
An app that is whitelisted can use the network and hold partial wake locks during Doze and App Standby. However, other restrictions still apply to the whitelisted app, just as they do to other apps. For example, the whitelisted app’s jobs and syncs are deferred (on API level 23 and below), and its regular AlarmManager alarms do not fire.
It appears your use case falls under "other restriction still apply".
Your application drains the battery when not actively in use, which is exactly the kind of behavior App Standby and Doze are meant to counteract.
Upvotes: 1