We are working on mobile tracking app. For location updates, Fused location API is used with High Accuracy as priority.
Location update is required even when the screen is off. So, we are using background Service
. Background Service
is also acquiring partial WakeLock
so that the device does not goes to sleep.
In the background Service
we request location updates with the pending update of the Service
.
The problem is that we are only receiving location updates when the screen is on. As soon as the screen is off location updates stops coming. Also there is a Thread
run by the Service
which is not getting kill at any point.
Creating the location request again when the screen is off by BroadcastReceiver
does not work either.
Here is the background Service
class (RouteExecution
):
private static final String TAG = "RouteExecution";
private static RouteExecution routeExecution = null;
private static GoogleApiClient mGoogleApiClient;
private static PendingIntent pendingIntent = null;
private PowerManager.WakeLock waitLock;
/**
* Creates an IntentService. Invoked by your subclass's constructor.
*/
public RouteExecution() {
super(TAG);
RouteExecution.routeExecution = this;
}
@Override
public void onCreate() {
super.onCreate();
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
BroadcastReceiver mReceiver = new PowerButtonReceiver();
registerReceiver(mReceiver, filter);
PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
waitLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
waitLock.acquire();
}
@Override
protected void onHandleIntent(Intent intent) {
if (LocationResult.hasResult(intent)) {
LocationResult locationResult = LocationResult.extractResult(intent);
Location location = locationResult.getLastLocation();
GPSLocation gpsLocation = new GPSLocation(location);
Log.d(TAG, "Location Accuracy: " + location.getAccuracy() + " "
+ " has: " + location.hasAccuracy() + " Provider: " + location.getProvider()
+ " long: " + location.getLongitude() + " lat: " + location.getLatitude());
}
}
public boolean checkLocationPermission() {
if (ActivityCompat.checkSelfPermission(routeExecution, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(routeExecution, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return false;
}
return true;
}
public void createLocationListener() {
if (!this.checkLocationPermission()) {
return;
}
LocationRequest mLocationRequest = LocationRequest.create();
mLocationRequest.setInterval(5000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setSmallestDisplacement(0);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
PendingResult<Status> statusPendingResult = LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, pendingIntent);
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.d(TAG, mGoogleApiClient.isConnected() + " On Connected");
synchronized (this) {
createLocationListener();
}
}
public static GoogleApiClient getmGoogleApiClient() {
return mGoogleApiClient;
}
@Override
public void onDestroy() {
super.onDestroy();
waitLock.release();
mGoogleApiClient.disconnect();
}
public static RouteExecution getRouteExecution() {
return routeExecution;
}
public static void setPendingIntent(PendingIntent pendingIntent) {
RouteExecution.pendingIntent = pendingIntent;
}
Service
is started using AlarmManager
. Here is the extraction:
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent updateServiceIntent = new Intent(context, RouteExecution.class);
PendingIntent pendingUpdateIntent = PendingIntent.getService(context, 0, updateServiceIntent, 0);
RouteExecution.setPendingIntent(pendingUpdateIntent);
alarmManager.set(AlarmManager.RTC_WAKEUP, 50000, pendingUpdateIntent);
BroadcastReceiver
:
public class PowerButtonReceiver extends BroadcastReceiver {
private static final String TAG = "PowerButton";
@Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Power Button");
if (RouteExecution.getRouteExecution() != null) {
RouteExecution.getRouteExecution().createLocationListener();
}
}
}
How to keep getting location updates even when the screen is off.
Thanks for the help.
First of all, aquiring a WakeLock
in the Service
is not reliable at all. The device can go back to sleep before that code gets executed.
I would call PendingIntent.getBroadcast()
instead of PendingIntent.getService()
, because a WakeLock
is technically guaranteed during onReceive()
, and start the Service
from there.
Either aquire a WakeLock
in onReceive()
, start your Service
and release the WakeLock
from the Service
, when appropriate
or
use WakefulBroadcastReceiver
, which basically does the same, without having to implement most of that logic yourself.
Another possible problem is using AlarmManager.set()
on Marshmallow devices.
Alarms set by this method will not get fired on these devices if Doze is active.
Use setAndAllowWhileIdle()
instead of set()
on API level 23+.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With