I have an android app in which I have used FusedLocationApi to get location updates of the user.
Following is the scenario:
1. I have a singleton Watcher class in which I define the pending intent to get the locations even when app is in background. Following is the code:
private PendingIntent pendingIntent;
private Watcher() {
Intent locationIntent = new Intent(context, Receiver.class);
pendingIntent = PendingIntent.getBroadcast(
context, 007 /*requestcode*/, locationIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
}
Then, when I have successfully connected location services, I request for location updates:
LocationServices.FusedLocationApi.
requestLocationUpdates(googleApiClient, locationRequest, pendingIntent);
Now, whenever Receiver class which extends WakefulBroadcastReceiver gets the location update, it starts the Service.class. The service class extends IntentService.
Following is the code:
#Override
synchronized protected void onHandleIntent(final Intent intent) {
if(LocationResult.hasResult(intent)) {
LocationResult locationResult= LocationResult.extractResult(intent);
Location location = locationResult.getLastLocation();
printLocation(location);
}
So my question is, given the above steps, why does the onHandleIntent gets woken up by the LocationReceiver multiple times within a period of 5 milliseconds. The lat, lng and accuracy are all the same. I have defined the
setFastestInterval(5 seconds); and
setInterval(1 minute);
Also the location accuracy is BALANCED_POWER_ACCURACY;
In my app, the
LocationServices.FusedLocationApi.
requestLocationUpdates(googleApiClient, locationRequest, pendingIntent);
do gets called multiple times. But according to the documentation: "Any previously registered requests that have the same PendingIntent (as defined by equals(Object)) will be replaced by this request." And I am using the same pendingIntent object to call requestLocationUpdates.
Thanks in advance.
Hi Kanika you can set the minimum displacement before passing your location request object this will help you to get updates only when you have certain minimum displacement. you can use below method for doing same.
setSmallestDisplacement(float smallestDisplacementMeters)
Also FYI api reference link , https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest
Related
I started with Googles LocationUpdatesPendingIntent example.
I've moved the location stuff from the Main Activity to the onBoot broadcast receiver as I need location updates to start straight away when the device boots. This works perfectly and provides a notification in the status bar.
But how do I go about turning the location updates on and off from an Activity?
This is for polling vehicle locations.
This is my BroadcastReceiver:
public class StartupComplete1 extends BroadcastReceiver {
private static final long UPDATE_INTERVAL = 10000; // Every 10 seconds.
private static final long FASTEST_UPDATE_INTERVAL = 5000; // Every 5 seconds
private static final long MAX_WAIT_TIME = UPDATE_INTERVAL * 2; // Every 20 seconds.
private LocationRequest mLocationRequest;
private FusedLocationProviderClient mFusedLocationClient;
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equalsIgnoreCase(Intent.ACTION_BOOT_COMPLETED)) {
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context);
createLocationRequest();
try {
mFusedLocationClient.requestLocationUpdates(mLocationRequest, getPendingIntent(context));
} catch (SecurityException e) {
Toast.makeText(context, "Error - Cant start location updates", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
}
}
private PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, LocationUpdatesBroadcastReceiver.class);
intent.setAction(LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES);
return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL);
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setMaxWaitTime(MAX_WAIT_TIME);
}
It's not a great idea to start checking for location updates in broadcast receiver. Based on android documentation for broadcast receiver
As a general rule, broadcast receivers are allowed to run for up to 10 seconds before they system will consider them non-responsive and ANR the app. Since these usually execute on the app's main thread, they are already bound by the ~5 second time limit of various operations that can happen there (not to mention just avoiding UI jank), so the receive limit is generally not of concern. However, once you use goAsync, though able to be off the main thread, the broadcast execution limit still applies, and that includes the time spent between calling this method and ultimately PendingResult.finish().
This could cause ANR when location updates takes longer time for responding, especially if you are indoors.
You should start a sticky service on Boot completed broadcast receiver's onReceive(). MainActivity can then bind to this service to perform the necessary action.
This approach could have issues if you are targeting Android O. Please check this post which explains background location gathering on Android O.
I'm triggering a BoradcastReceiver when receiving a location update
PendingIntent pendingIntent = PendingIntent
.getBroadcast(this, 54321, intent, PendingIntent.FLAG_CANCEL_CURRENT);
LocationServices.FusedLocationApi.requestLocationUpdates(this.mGoogleApiClient,
mLocationRequest, pendingIntent);
And my Receiever
public static class LocationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
boolean hasLocation = LocationResult.hasResult(intent);
}
}
If I run the above code eveything works fine hasLocation is always true, perfect.
But If I wish to pass some variable to the Receiver so I do:
Intent intent = ..
intent.putExtra("test", "hello");
PendingIntent pendingIntent = PendingIntent
.getBroadcast(this, 54321, intent, PendingIntent.FLAG_CANCEL_CURRENT);
Bit now in the reciever LocationResult.hasResult(intent); is always false
Is this a bug? Is there a workaround to this? How can I pass variable to the reciever?
I found your question while googling. I will share my solution in case anyone else finds this question like me.
First, this is my situation, which is similar to yours:
requestLocationUpdates() stores the location data in the mExtras field of the intent. For some reason, if I add another Extra to the Intent using Intent.putExtra(), the location data is not added. So onHandleIntent() is called, but the intent is missing the location data. If I don't add any Extras, then the location data comes through and everything is fine.
My workaround:
I used Intent.addCategory() and getCategory() to do the exact same thing as putExtra("myExtraName", String). If you want to pass other data types, convert them to a string then parse them in onHandleIntent().
My environment:
I am using Play Services version 11.0.4 and FusedLocationProviderClient, since FusedLocationProviderApi has been deprecated recently. The FusedLocationProviderClient.requestLocationUpdates() documentation doesn't seem to address this.
So this code worked on friday:
public void requestLocationUpdate() {
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
Intent updateIntent = new Intent(SINGLE_LOCATION_UPDATE_ACTION);
PendingIntent singleUpdatePI = PendingIntent.getBroadcast(_context, 0, updateIntent, PendingIntent.FLAG_UPDATE_CURRENT);
IntentFilter locIntentFilter = new IntentFilter(SINGLE_LOCATION_UPDATE_ACTION);
SingleUpdateReceiver receiver = new SingleUpdateReceiver();
getApplicationContext().registerReceiver(receiver, locIntentFilter);
locationManager.requestSingleUpdate(criteria, singleUpdatePI);
}
class SingleUpdateReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// ... never invoked
}
}
Today, onReceive() simply will not be invoked. Is there any reason why this shouldn't work today? The only thing changed is that I'm on a different WiFi router now.
I can see a couple of suspicious lines in LogCat:
06-23 15:10:36.540 709-7716/? D/LocationManagerService﹕ request 430483d8 fused Request[ACCURACY_BLOCK fused requested=+10m0s0ms fastest=+10m0s0ms num=1] from com.nilzor.app(10195)
06-23 15:10:36.540 709-7716/? D/LocationManagerService﹕ provider request: fused ProviderRequest[ON interval=+10m0s0ms]
...don't know if that is relevant?
The documentation of requestSingleUpdate points me to requestLocationUpdates, which again tells me that it might take a while to get the first update. Is that true also for requestSingleUpdate? On friday, the callback fired within a second of the request. Today I've tried 10+ times and it havent fired once. 5 minutes counting as I post now.
When you connect to wi-fi you get coordinates from your router. It does not matter where you are located, in your wi-fi area/place use real gps, or gps spoiler.
I have searched quite a bit and I'm not totally clueless. I have implemented a temporary solution on my end but was wondering if there is a better approach out there.
I have an app that sends a person's location after every 60 seconds to a server. On my dashboard (the main screen that will go to onPause after application starts), I have registered a LocationManager with the following code:
service = (LocationManager) getSystemService(LOCATION_SERVICE);
boolean enabled = service
.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!enabled)
{
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(intent);
}
else
{
Criteria criteria = new Criteria();
provider = service.getBestProvider(criteria, false);
service.requestLocationUpdates(provider, 10000, 50, this);
Location location = service.getLastKnownLocation(provider);
// Initialize the location fields
if (location != null)
{
onLocationChanged(location);
}
else
{
Log.d("Location: ", "No update received");
}
}
However, as I mentioned, this activity will be minimized by the user (by pressing the home button). There is a service that gets called every 60 seconds by an AlarmManager. That service accesses static variables from the Dashboard Activity (lat, lon) and sends it to the server.
My question:
If the activity goes onPause, will the requestLocationUpdates function stop? Or will it keep working?
If it keeps working, it will keep updating the two lat and lon static String objects and the service will keep getting updated values. If they stop, the service will keep getting the same old values again and again.
Also, is there a better way to approach this problem? Using a mix of GPS Provider and Network Provider? (I need fairly accurate values).
EDIT
Here's my Alarm. This code is inside Login Activity
Intent i = new Intent(con, LocationPoller.class);
i.putExtra(LocationPoller.EXTRA_INTENT, new Intent(con,
Login.class));
i.putExtra(LocationPoller.EXTRA_PROVIDER,
LocationManager.GPS_PROVIDER);
gps = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
PendingIntent pi = PendingIntent.getBroadcast(con, 0, i, 0);
gps.setRepeating(AlarmManager.RTC_WAKEUP, c.getTimeInMillis(),
10 * 1000, pi);
Log.d("Service: ",
"GPS Service started and scheduled with AlarmManager");
Here's my receiver (also within Login activity)
private class ReceiveMessages extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Location loc = (Location) intent.getExtras().get(
LocationPoller.EXTRA_LOCATION);
String msg;
if (loc == null)
{
msg = intent.getStringExtra(LocationPoller.EXTRA_ERROR);
}
else
{
msg = loc.toString();
}
if (msg == null)
{
msg = "Invalid broadcast received!";
}
Log.d("GPS Broadcast: ", msg);
}
}
Nothing's happening :s Not getting anything on logcat which means the broadcast isn't being received.
When activity goes on pause, all registered listeners will stop. Better way to implement this is, alarm manager sent a broadcast every 60 seconds, this broadcast receiver starts a service and this service will request a location on Wakeful thread, once location information is retrieved, update the location on server.
There is an Open source library available with an example (courtesy CommonsWare), please refer below link. Its under Apache 2.0 license
Location Polling Library
Please find my sample project using above library. I have modified few things in the above library and created my own version.
Location Polling Demo Application
this is my very first question:
I'm trying to configure proximity alerts that will be feed from a Database and webservice provider; but I have a problem configuring a simple proximity alert for testing. I manage to create the alert but it never gets fired, I'm only trying in the emulator for now and don´t know if I need some extra code to trigger the alerts.
I've read somewhere that the GPS provider to disabled so the network provider can be used in order to trigger the alerts on the emulator.
My code looks like this:
Proximity intent declaration
private String proximityIntentAction = new String("gpsdelivery.gpship.getLocation.GPS_PROXIMITY_ALERT");
Inside onStart() where the parameters of the alerts are set
IntentFilter intentFilter = new IntentFilter(proximityIntentAction);
registerReceiver(new ProximityAlert(), intentFilter);
setProximityAlert(45.150344, 9.999815, -1);
Proximity alert function where the alerts get created
private void setProximityAlert(double lat, double lon, int requestCode)
{
// 100 meter radius
float radius = 100f;
// Expiration is 10 Minutes
long expiration = 600000;
LocationManager locManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Intent intent = new Intent(proximityIntentAction);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), requestCode, intent, PendingIntent.FLAG_CANCEL_CURRENT);
locManager.addProximityAlert(lat, lon, radius, expiration, pendingIntent);
}
And finally my proximity alert class with the Broadcast receiver
public class ProximityAlert extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Log.v("SomeTag","Proximity alert received");
}
}
Please let me know what I'm missing or what am I doing wrong. Thanks in advance, any source code would be appreciated.
Well, I believe your setProximityAlert(45.150344, 9.999815, -1); is incorrect. Specifically, your -1. It doesn't contain the information needed in the locManager.addProximityAlert(lat, lon, radius, expiration, pendingIntent);.