I have developed an android app which checks location updates and sends location data to server after a certain time. I tested both methods; one using Google location apI and another using Android service. But both apps are consuming too much battery. I need to optimise the use of battery, so that users can use the app as long as they need.
There are several apps which track our location all the time and never hamper the battery; for example endomondo, Facebook etc. Specially I want to mention endomondo because it really works fine while working.
Can anyone please suggest me the best way to implement location awareness for android app?
How can I make my app like endomondo or similar location aware app?
Please suggest me the better way to do this.
Thanks in advance.
Have you tried to reduce the GPS location tracking by check if the LocationManagar has Location that good enough for you?(Maybe other application tracking?)
Ex:
private static Location bestLastKnownLocation(float minAccuracy, long maxAge, LocationManager mLocationManager) {
Location bestResult = null;
float bestAccuracy = Float.MAX_VALUE;
long bestAge = Long.MIN_VALUE;
List<String> matchingProviders = mLocationManager.getAllProviders();
for (String provider : matchingProviders) {
Location location = mLocationManager.getLastKnownLocation(provider);
if (location != null) {
float accuracy = location.getAccuracy();
long time = location.getTime();
if (accuracy < bestAccuracy) {
bestResult = location;
bestAccuracy = accuracy;
bestAge = time;
}
}
}
// Return best reading or null
if (bestAccuracy > minAccuracy || (System.currentTimeMillis() - bestAge) > maxAge) {
return null;
} else {
return bestResult;
}
}
Related
I am working on a android project where i have to get the current location of the user for every 2 minutes and the code we are using is
try {
DpGPSEnabled = DpLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
DpNetworkEnabled = DpLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
if (DpIsGpsProvider && DpGPSEnabled) {
DpLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER,DpMIN_TIME_BW_UPDATES,DpMIN_DISTANCE_CHANGE_FOR_UPDATES,DpLocationListener);
} else if (!DpGPSEnabled) {
DpLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER,DpMIN_TIME_BW_UPDATES,DpMIN_DISTANCE_CHANGE_FOR_UPDATES,DpLocationListener);
DpGenUtil.setLocationPostion(DpLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER));
}
if (DpGPSEnabled && DpNetworkEnabled) {
DgProvider = "GPS+Network";
} else if (DpNetworkEnabled) {
DgProvider = "Network";
} else if (DpGPSEnabled) {
DgProvider = "GPS";
} else {
DgProvider = "Not Selected";
}
} catch (Exception ex) {
}
where DpMIN_TIME_BW_UPDATES =1000*60*2; 2 minutes
and DpMIN_DISTANCE_CHANGE_FOR_UPDATES = 200; in meters
the problem is we are able to get the locations properly , but battery started to drain faster , is there any other method we can get the location and it will not consume much battery also.
Gps consumes more battery than wifi. You can request location updates using Criteria with LocationManager or you can use FusedLocationProvider api if not having Google Play Services is not an issue. FLP uses Wifi, Gps and Kalman filters for location accuracy. You can test both LocationManager and FLP for accuracy and battery consumption. I get up to 4m accuracy(better devices may get better results) on outdoors from both LocationManager Gps and FLP. LocationManager wifi has less accuracy and does not have speed, altitude info.
I am working on a project where we are trying to track the position of a device and keep the data for later use. Before I talk about the issue I would like to provide some background.
By searching around StackExchange and Google and everywhere else, I have come to the conclusion that it is virtually impossible to get information about the satellites using the Fused Location API (good job there Google).
The method that most people are using is to actually use a LocationManager along side the Fused location to get the GPS Status. My first question comes here:
How can we be 100% sure that the numbers provided by the LocationManager are in sync with what the Fused Location has given us? Does the Fused Location use the Manager internally?
And now the issue. The app is using an "always on" sticky service to pick up the positions no matter what. When there are no satellites everything works as intended. Placing the device to a position where it can see satellites it does not seem to have a lock. Using the debugger the GpsStatus.getSatellites() brings an empty list. Now, without moving the device I start the app Compass (by Catch.com as there are many) that has a GPS type compass scheme. That one locks the satellites, and quite fast, and from that moment on my app also reports the satellites. If the compass is closed then the app gets stuck on the last number the Compass was providing!!! The device I am personally using for testing is a Nexus 7 2013 with its latest official updates (Android 6.0.1).
Here is some code:
public class BackgroundLocationService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
GpsStatus.Listener,
LocationListener {
// Constants here....
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private LocationManager locationManager;
// Flag that indicates if a request is underway.
private boolean mInProgress;
private NotificationManagement myNotificationManager;
private Boolean servicesAvailable = false;
//And other variables here...
#Override
public void onCreate()
{
super.onCreate();
myNotificationManager = new NotificationManagement(getApplicationContext());
myNotificationManager.displayMainNotification();
mInProgress = false;
// Create the LocationRequest object
mLocationRequest = LocationRequest.create();
// Use high accuracy
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// Set the update interval
mLocationRequest.setInterval(PREFERRED_INTERVAL);
// Set the fastest update interval
mLocationRequest.setFastestInterval(FASTEST_INTERVAL);
servicesAvailable = servicesConnected();
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
locationManager.addGpsStatusListener(this);
setUpLocationClientIfNeeded();
}
/**
* Create a new location client, using the enclosing class to
* handle callbacks.
*/
protected synchronized void buildGoogleApiClient()
{
this.mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
private boolean servicesConnected()
{
// Check that Google Play services is available
int resultCode = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode)
{
return true;
}
else
{
return false;
}
}
public int onStartCommand(Intent intent, int flags, int startId)
{
super.onStartCommand(intent, flags, startId);
if (!servicesAvailable || mGoogleApiClient.isConnected() || mInProgress)
return START_STICKY;
setUpLocationClientIfNeeded();
if (!mGoogleApiClient.isConnected() || !mGoogleApiClient.isConnecting() && !mInProgress)
{
mInProgress = true;
mGoogleApiClient.connect();
}
return START_STICKY;
}
private void setUpLocationClientIfNeeded()
{
if (mGoogleApiClient == null)
buildGoogleApiClient();
}
public void onGpsStatusChanged(int event)
{
}
// Define the callback method that receives location updates
#Override
public void onLocationChanged(Location location)
{
simpleGPSFilter(location);
}
// Other fancy and needed stuff here...
/**
* "Stupid" filter that utilizes experience data to filter out location noise.
* #param location Location object carrying all the needed information
*/
private void simpleGPSFilter(Location location)
{
//Loading all the required variables
int signalPower = 0;
satellites = 0;
// Getting the satellites
mGpsStatus = locationManager.getGpsStatus(mGpsStatus);
Iterable<GpsSatellite> sats = mGpsStatus.getSatellites();
if (sats != null)
{
for (GpsSatellite sat : sats)
{
if (sat.usedInFix())
{
satellites++;
signalPower += sat.getSnr();
}
}
}
if (satellites != 0)
signalPower = signalPower/satellites;
mySpeed = (location.getSpeed() * 3600) / 1000;
myAccuracy = location.getAccuracy();
myBearing = location.getBearing();
latitude = location.getLatitude();
longitude = location.getLongitude();
Log.i("START OF CYCLE", "START OF CYCLE");
Log.i("Sat Strength", Integer.toString(signalPower));
Log.i("Locked Sats", Integer.toString(satellites));
// Do the math for the coordinates distance
/*
* Earth's radius at given Latitude.
* Formula: Radius = sqrt( ((equatorR^2 * cos(latitude))^2 + (poleR^2 * sin(latitude))^2 ) / ((equatorR * cos(latitude))^2 + (poleR * sin(latitude))^2)
* IMPORTANT: Math lib uses radians for the trigonometry equations so do not forget to use toRadians()
*/
Log.i("Lat for Radius", Double.toString(latitude));
double earthRadius = Math.sqrt((Math.pow((EARTH_RADIUS_EQUATOR * EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2))
/ (Math.pow((EARTH_RADIUS_EQUATOR * Math.cos(Math.toRadians(latitude))), 2)
+ Math.pow((EARTH_RADIUS_POLES * Math.cos(Math.toRadians(latitude))), 2)));
Log.i("Earth Radius", Double.toString(earthRadius));
/*
* Calculating distance between 2 points on map using the Haversine formula (arctangent writing) with the following algorithm
* latDifference = latitude - lastLatitude;
* lngDifference = longitude - lastLongitude;
* a = (sin(latDifference/2))^2 + cos(lastLatitude) * cos(latitude) * (sin(lngDifference/2))^2
* c = 2 * atan2( sqrt(a), sqrt(1-a) )
* distance = earthRadius * c
*/
double latDifference = latitude - lastLatitude;
double lngDifference = longitude - lastLongitude;
double a = Math.pow((Math.sin(Math.toRadians(latDifference / 2))), 2) + (Math.cos(Math.toRadians(lastLatitude))
* Math.cos(Math.toRadians(latitude))
* Math.pow((Math.sin(Math.toRadians(lngDifference / 2))), 2));
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
double distance = earthRadius * c;
Log.i("New point distance", Double.toString(distance));
// Filter logic
// Make an initial location log
if ((!isInit) && (myAccuracy < ACCEPTED_ACCURACY))
{
isInit = true;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
}
else
{
// Satellite lock (use of GPS) on the higher level
if (satellites == 0)
{
// Accuracy filtering at the second level
if (myAccuracy < ACCEPTED_ACCURACY)
{
if ((distance > ACCEPTED_DISTANCE))
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "No Sats");
/*
// Calculate speed in correlation to perceived movement
double speed = distance / (PREFERRED_INTERVAL / 1000); // TODO: Need to make actual time dynamic as the fused location does not have fixed timing
if (speed < ACCEPTED_SPEED)
{
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
} */
}
}
}
else if ((satellites < 4) && (signalPower > ACCEPTED_SIGNAL))
{
if (myAccuracy < (ACCEPTED_ACCURACY + 50))
{
logLocations(location);
Log.i("Location Logged", "With Sats");
}
}
else
{
if (myAccuracy < (ACCEPTED_ACCURACY + 100))
{
lastSpeed = mySpeed;
lastBearing = myBearing;
lastLatitude = latitude;
lastLongitude = longitude;
logLocations(location);
Log.i("Location Logged", "With Good Sats");
}
}
}
Log.i("END OF CYCLE", "END OF CYCLE");
}
private void logLocations(Location location)
{
String myprovider = "false";
String temp = timestampFormat.format(location.getTime());
MySQLiteHelper dbHelper = new MySQLiteHelper(getApplicationContext());
try
{
dbHelper.createEntry(latitude, longitude, allschemes, temp, mySpeed, myAccuracy, myBearing, myprovider, satellites);
}
catch (Exception e)
{
e.printStackTrace();
}
CheckAutoArrive(String.valueOf(latitude), String.valueOf(longitude));
}
This is the part of the code I think might be needed. I am leaving all the filtering code there along with the math to compute Earth's radius given the latitude and the distance between 2 points on the map. Feel free to use that if you need it.
In connection to the fact that the Compass app can actually make the system get satellites while my app cannot. Is there a way to actually force read the location services? Is it possible that the Fused Location actually uses GPS but the Location Manager does not know it?
Finally I would like to mention that the application has been tested in other devices (phones, not tablets) with different versions of Android and seems to be working properly.
Any ideas would be more than welcome. And of course go ahead and ask anything I might have forgotten to mention.
EDIT : My actual questions were hidden in the text so to lay them out:
1) Are the Location data we get from Fused Location and the rest of the GPS data we can, seemingly, only get from the Location Manager in sync or is there the possibility to get a Location but wrong number of locked satellites for the particular point?
2) What could be the reason behind the weird behavior where the application cannot get a lock to satellites but if the lock comes from another application it seems to be used properly by the application? To make this even weirder this happens to a Nexus 7 (Android 6.0.1) but not to other devices tested with different Android versions.
From my understanding:
1)
The FusedLocationApi returns a new location every time there is a new reading from any relevant provider on the client device (WiFi, CellTower, GPS, Bluetooth). This reading gets fused with the previous location estimate (probably using an extended Kalman filter or similar). The resulting location update is a fused estimate of several sources, which is why there is no metadata from individual providers attached.
So the Location data you get from the API may coincide with a pure GPS reading obtained from the LocationManager (if GPS was the most recent and relevant location source), but it doesn't have to. Consequently, the number of satellites obtained from the last pure GPS reading may apply to the latest Location returned by the FusedLocationApi or it may not.
In short:
There is no guarantee that a location reading obtained from the LocationManager is in sync with a location from the FusedLocationApi.
2)
First off: To pinpoint the root cause of this issue you need to test with several devices at several locations. Since you asked
What could be the reason behind the weird behavior?
I'll throw one theory out there: Assuming that the LocationManager and the FusedLocationApi work completely separately, the LocationManager may be struggling to obtain a fix because you're relying on GPS only.
Try using the NETWORK_PROVIDER in addition to GPS to speed up the time-to-first-fix (thus enabling the LocationManager to make use of Assisted GPS). Other apps (like the Compass app) are almost certainly doing this, which would explain why they are getting a quicker fix.
Note: After you start to receive GPS data, you can of course unregister the network provider. Or you keep it on but simply ignore its updates.
That's one possible explanation for the weird behavior. You are probably aware that location behavior is dependent on the device, OS, GPS chipset, firmware and location you are in - so if you plan to go manual (i.e. not use the FusedLocationApi) you will have to experiment quite a bit.
In addition to the answers, let me provide an opinionated take on your problem (to be taken with a grain of salt ;-): I think you're trying to combine two things that were made for very different use cases and aren't meant to be combined.
Obtaining the number of satellites is something utterly technical. No end user will ever be interested in this kind of info, unless your application teaches them about GNSS. If you want to record it for internal analytics purposes that's fine, but then you must be able to deal with situations where this info is not available.
Scenario 1: For some reason you decide that you absolutely need the detailed (technical) specifics of GPS readings. In that case, build the logic yourself, the old school way. I.e. ask for GPS readings (and possibly speed up this process by using the network provider) via the LocationManager, then fuse this stuff on your own. However, in this scenario don't ever touch the FusedLocationApi.
Even though it may seem old-fashioned and arcane nowadays, using the LocationManager with DIY fusion logic still makes perfect sense for a small number of use cases. That's why the API is still around.
Scenario 2: You simply want to get quick and accurate updates on the client's location. In that case specify your desired update frequency and accuracy and let the FusedLocationApi do its job. The FusedLocationApi has come a long way in the past years and chances are that nowadays it will be quicker and better at figuring out how to obtain location info than any DIY logic. This is because obtaining location info is a very heterogenous problem that depends on the capabilities of the client device (chipset, firmware, OS, GPS, WiFi, GSM/LTE, Bluetooth etc.) as much as on the physical surroundings (WiFi/CellTower/Bluetooth signals nearby, inside or outside, clear sky or urban canyon etc.).
In this scenario, don't touch the manual providers. If you do, don't expect to make any meaningful inferences on the relation between the readings of individual providers and the fused results.
Two final remarks:
Android provides Location.distanceTo() and Location.distanceBetween(), so there is no need to implement the Haversine formula in your code.
If you simply need quick and reliable updates from the FusedLocationApi, I have written a small utility class, called the LocationAssistant, that simplifies the setup and does most of the heavy lifting for you.
I am trying to get my current coordinates with network provider and not gps provider.
I was able to figure out the solution for that but I am a bit confused with the concept in this scenario.
Working Code
Here's my code for getting my coordinates:
public void getLocation(){
locationManager = (LocationManager)getSystemService(LOCATION_SERVICE);
if(appUtils.isOnline()){
try{
Geocoder geocoder = new Geocoder(
MainActivity.this.getApplicationContext(),
Locale.getDefault());
Location locationNetwork = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
List<Address> list;
if(locationNetwork!=null){
Toast.makeText(context,"Network Available!!",Toast.LENGTH_LONG).show();
list = geocoder.getFromLocation(locationNetwork.getLatitude(),locationNetwork.getLongitude(),3);
if(list!=null&&list.size()>0){
latitude = list.get(0).getLatitude();
longitude = list.get(0).getLongitude();
Toast.makeText(context,String.valueOf(latitude) + " (....) " + String.valueOf(longitude),Toast.LENGTH_LONG).show();
int count = 0;
while (latitude==null||longitude==null){
latitude = list.get(count).getLatitude();
longitude = list.get(count).getLongitude();
count++;
Toast.makeText(context,String.valueOf(latitude) + " --- " + String.valueOf(longitude),Toast.LENGTH_LONG).show();
}
}
}else{
Toast.makeText(context,"No response!!",Toast.LENGTH_LONG).show();
}
}catch (IOException e){
e.printStackTrace();
}
}else{
Toast.makeText(context,"Server not responding",Toast.LENGTH_LONG).show();
}
}
This piece of code is working perfectly fine when the gps is enabled. If gps is disabled, it doesn't work.
Now, if we are setting the location to NETWORK_PROVIDER:
Location locationNetwork = locationManager
.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
Why do we still require gps ?
Now if I change it to PASSIVE PROVIDER:
Location locationNetwork = locationManager
.getLastKnownLocation(LocationManager.PASSIVE_PROVIDER);
It works fine with the passive provider. Why is it that ?
Can someone explain what is the major difference here and what would be the right way to get the coordinates with network provider ?
I know this question is been asked several times and I did went through it. I just want to get cleared with this concept.
Thank's in advance.. :)
It doesn't require GPS to use the network provider, I've done it many times. However, getLastKnowLocation may not return a value if either it has never had an app request updates for that provider, or if the last time that happened was too long ago. You cannot count on that function always returning non-NULL. If you want to ensure that you get a location, use requestSingleUpdate instead. This will always get you a location (assuming the provider you use is enabled), but may take some time- a result may not be immediately available.
(There is one other time that function may never return- if you use the GPS provider and it can't get a lock on enough sattelites to find a location. Such as if you're in an underground parking garage).
This is the bit of code that I use to quickly get the current location, by checking all available network options.
private double[] getGPS(){
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
List<String> providers = lm.getProviders(true);
/* Loop over the array backwards, and if you get an accurate location, then break out the loop*/
Location l = null;
for (int i=providers.size()-1; i>=0; i--) {
l = lm.getLastKnownLocation(providers.get(i));
if (l != null) break;
}
double[] gps = new double[2];
if (l != null) {
gps[0] = l.getLatitude();
gps[1] = l.getLongitude();
}
return gps;
}
I'm looking at writing a location-aware application for Android. My application would periodically make calls to a central server, passing in the current user's location. I don't want to drain the battery, so I've been considering using the Passive Location Provider.
According to the provider description no active polling is performed (hence being labelled "Passive"). Instead, it relies on other applications requesting active polls.
My question is: If no other applications poll for Location, does it mean the Passive provider never provides any data? Or, does the Android OS itself periodically poll for Location. If so, what is the polling frequency?
Thanks
You can use the Google Play Services Location API. Its fused location provider will relieve you from such concerns. Forget about which providers to use, or how often the location is polled, or if there are other apps polling. Instead, specify high-level needs like "high accuracy" or "low power", and at which interval your app should be notified. Use listeners or intents to receive location updates from the location service. Here's a more recent article with some code.
AFAIK Android OS will not poll for location itself. If some apps polls it you can received the location then. See Android - Are there query-able services that already poll for GPS location? .
If no app has polled for location from a long time, last location known might be returned by the passive provider. If your application relies heavily on the location, then you might actually want to poll it yourself or if nothing is returned by the passive provider then you can yourself get the location. Frequency of getting the location can be 5 minutes( which is suggested by Google in the Android documentation). This frequency can vary based on your app's requirement.
Is you read the android documentation of requestLocationUpdates of LocationManager, it says:
Choosing a sensible value for minTime is important to conserve battery life. Each location update requires power from GPS, WIFI, Cell and other radios. Select a minTime value as high as possible while still providing a reasonable user experience. If your application is not in the foreground and showing location to the user then your application should avoid using an active provider (such as NETWORK_PROVIDER or GPS_PROVIDER), but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes) or greater. If your application is in the foreground and showing location to the user then it is appropriate to select a faster update interval.
That's my 2 cents regarding your question.
Android devices themselves never poll for users location, you need to do it yourself or rely on other apps to do it for you. You can possibly run the location update every 'x' min, using a GPS or network provider whatever deems fit (or maybe even both !)
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1500000, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1500000, 0, locationListener);
If you want to wait for other applications, or not want to drain the battery/search for users exact location you may use locationManager.getLastLocation();
This wont be always accurate, however it is the best you can hope for without actually running a location search
Though I don't believe that PASSIVE_LOCATION PROVIDERS will not get any location unless no other apps get a location, this is some post which is evangelizing it.
I believe that there would be some resident services part of the OS itself which listen to location changed events. because location services without maps, generally use GPS. But I recommend you to have a look at this discussion (probably trusted source).
as none of the posts here answer the OP question i'll make my three words here -
IT'S UP TO PROVIDER (TO PROVIDE THE LOCATION UPDATES) THUS PROVIDER DECIDES HOW OFTEN WILL REPORT THE LOCATION CHANGES TO LOCATON SERVICE
PASIVE - LIKE A PING PONG - to understand what it mean study flow of bellow methods
getLastLocation(LocationRequest request, String packageName)
reportLocation(Location location, boolean passive) -> updateLastLocationLocked(...) -> handleLocationChangedLocked(...)
ITS DEVELOPER JOB TO FILTER AND DECIDE IF PROVIDED LOCATION FULFILLS IT NEEDS
ad 3/ see all location provied details like:
PROVIDER NAME
ACCURACY,
TIME (MILLIS - EPOCH TIME),
SYSTEM ELAPSED NANOS (ELAPSED NANOS FROM DEVICE START)
ADDITIONAL DATA (EG IN BUNDLE LIKE SATELITES COUNT)
ETC...
ad 2/ LOCATION SERVICE IMPLEMENTATION ON AOSP:
package com.android.server;
/**
* The service class that manages LocationProviders and issues location
* updates and alerts.
*/
public class LocationManagerService extends ILocationManager.Stub {
...
// mapping from provider name to last known location
private final HashMap<String, Location> mLastLocation = new HashMap<>();
// same as mLastLocation, but is not updated faster than LocationFudger.FASTEST_INTERVAL_MS.
// locations stored here are not fudged for coarse permissions.
private final HashMap<String, Location> mLastLocationCoarseInterval = new HashMap<>();
...
#Override
public Location getLastLocation(LocationRequest request, String packageName) {
...
// Figure out the provider. Either its explicitly request (deprecated API's),
// or use the fused provider
String name = request.getProvider();
if (name == null) name = LocationManager.FUSED_PROVIDER;
LocationProviderInterface provider = mProvidersByName.get(name);
if (provider == null) return null;
...
Location location;
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
// Make sure that an app with coarse permissions can't get frequent location
// updates by calling LocationManager.getLastKnownLocation repeatedly.
location = mLastLocationCoarseInterval.get(name);
} else {
location = mLastLocation.get(name);
}
if (location == null) {
return null;
}
if (allowedResolutionLevel < RESOLUTION_LEVEL_FINE) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
if (noGPSLocation != null) {
return new Location(mLocationFudger.getOrCreate(noGPSLocation));
}
} else {
return new Location(location);
}
}
return null;
....
private void handleLocationChangedLocked(Location location, boolean passive) {
if (D) Log.d(TAG, "incoming location: " + location);
long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
// Skip if the provider is unknown.
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) return;
updateLastLocationLocked(location, provider);
// mLastLocation should have been updated from the updateLastLocationLocked call above.
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
Log.e(TAG, "handleLocationChangedLocked() updateLastLocation failed");
return;
}
// Update last known coarse interval location if enough time has passed.
Location lastLocationCoarseInterval = mLastLocationCoarseInterval.get(provider);
if (lastLocationCoarseInterval == null) {
lastLocationCoarseInterval = new Location(location);
mLastLocationCoarseInterval.put(provider, lastLocationCoarseInterval);
}
long timeDiffNanos = location.getElapsedRealtimeNanos()
- lastLocationCoarseInterval.getElapsedRealtimeNanos();
if (timeDiffNanos > LocationFudger.FASTEST_INTERVAL_MS * NANOS_PER_MILLI) {
lastLocationCoarseInterval.set(location);
}
// Don't ever return a coarse location that is more recent than the allowed update
// interval (i.e. don't allow an app to keep registering and unregistering for
// location updates to overcome the minimum interval).
Location noGPSLocation =
lastLocationCoarseInterval.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
// Skip if there are no UpdateRecords for this provider.
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null || records.size() == 0) return;
// Fetch coarse location
Location coarseLocation = null;
if (noGPSLocation != null) {
coarseLocation = mLocationFudger.getOrCreate(noGPSLocation);
}
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
// Get latest status
Bundle extras = new Bundle();
int status = p.getStatus(extras);
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> deadUpdateRecords = null;
// Broadcast location or status to all listeners
for (UpdateRecord r : records) {
...
}
...
}
/**
* Updates last location with the given location
*
* #param location new location to update
* #param provider Location provider to update for
*/
private void updateLastLocationLocked(Location location, String provider) {
Location noGPSLocation = location.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
Location lastNoGPSLocation;
Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
lastLocation = new Location(provider);
mLastLocation.put(provider, lastLocation);
} else {
lastNoGPSLocation = lastLocation.getExtraLocation(Location.EXTRA_NO_GPS_LOCATION);
if (noGPSLocation == null && lastNoGPSLocation != null) {
// New location has no no-GPS location: adopt last no-GPS location. This is set
// directly into location because we do not want to notify COARSE clients.
location.setExtraLocation(Location.EXTRA_NO_GPS_LOCATION, lastNoGPSLocation);
}
}
lastLocation.set(location);
}
}
PASIVE PROVIDER:
/**
* A passive location provider reports locations received from other providers
* for clients that want to listen passively without actually triggering
* location updates.
*
* {#hide}
*/
public class PassiveProvider implements LocationProviderInterface {
...
#Override
public void setRequest(ProviderRequest request, WorkSource source) {
mReportLocation = request.reportLocation;
}
public void updateLocation(Location location) {
if (mReportLocation) {
try {
// pass the location back to the location manager
mLocationManager.reportLocation(location, true);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling reportLocation");
}
}
}
...
}
My app only needs a coarse location service when started up.
In detail, I need the app's rough location so as to provide the users with the shop info nearby.
The location does NOT need to be updated constantly. In addition, coarse localization will be sufficient in this case.
I wish the app to choose GSM, or wifi, or GPS automatically, whichever is available.
The location service should also be one-time to save phone energy.
How may I do that?
I have tried using GPS separately.
My problem is I don't know how to stop the constantly-refreshing-location feature of GPS. I don't know how to make the phone select one out of the three methods, either.
Some sample codes or ideas are greatly appreciated.
Here's a certain point of view:
private void _getLocation() {
// Get the location manager
LocationManager locationManager = (LocationManager)
getSystemService(LOCATION_SERVICE);
Criteria criteria = new Criteria();
String bestProvider = locationManager.getBestProvider(criteria, false);
Location location = locationManager.getLastKnownLocation(bestProvider);
try {
lat = location.getLatitude();
lon = location.getLongitude();
} catch (NullPointerException e) {
lat = -1.0;
lon = -1.0;
}
}
This might however request a FINE_LOCATION access. So:
Another way is to use this which uses the LocationManager.
The quickest possible way is to use the Last Known location with this, I used it and it's quite fast:
private double[] getGPS() {
LocationManager lm = (LocationManager) getSystemService(
Context.LOCATION_SERVICE);
List<String> providers = lm.getProviders(true);
Location l = null;
for (int i=providers.size()-1; i>=0; i--) {
l = lm.getLastKnownLocation(providers.get(i));
if (l != null) break;
}
double[] gps = new double[2];
if (l != null) {
gps[0] = l.getLatitude();
gps[1] = l.getLongitude();
}
return gps;
}
you can use LocationClient it provides a unified/simplified (Fused) location API, check out this Video for introductory material and background or this developer guide
the main drawback is that you make you app dependent on the existence of Google Play Services on the device.
In order to get info from specific provider you should use: LocationManager.getLastKnownLocation(String provider), if you want your app to choose automatically between providers then you can add choosing of the provider with getBestProvider method. As for stopping refreshing of GPS location, I didn't quite catch. Do you need to get the location info only once or do you need to monitor location changes periodically?
EDIT: Oh by the way, if you want your location info to be up-to-date you should use requestSingleUpdate method of location manager, with specified Criteria. In this case provider should also be chosen automatically