简体   繁体   中英

Get user's location via GPS then Network then Wifi

I have a service at the moment that sort of works. It checks if GPS is enabled, if it is it will get the GPS location and my map can zoom to the location. It has getLastKnownLocation . Problem is, getLastKnownLocation might be miles away(as it was yesterday when I tried it).

It runs the GPS check first, as it's enabled it doesn't run the network check for location.

Is there a way for to have it so that if GPS is enabled, but can't get a fix other than getLastKnownLcation() is will default to a network based location? After that I will check so that if the network is not enabled or lastKnownLocation is too far away I can check for Wifi.

Here is my code for the service:

public class GPSTracker extends Service implements LocationListener {

private final Context mContext;
boolean isGPSEnabled = false;
boolean isNetworkEnabled = false;
boolean canGetLocation = false;

Location location;
double latitude;
double longitude;

//Minimum distance for update
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; //10 meters

// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 1000 * 40; //40 seconds

protected LocationManager locationManager;

public GPSTracker(Context context) {
    this.mContext = context;
    getLocation();
}

public Location getLocation() {
    Log.i("i", "Get location called");

    locationManager = (LocationManager) mContext.getSystemService(LOCATION_SERVICE);

    //Getting GPS status
    isGPSEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);

    //Getting network status
    isNetworkEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    this.canGetLocation = true;
    if (isGPSEnabled) {
        if (location == null) {
            locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
            Log.d("GPS Enabled", "GPS Enabled");
            if (locationManager != null) {
                location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                if (location != null) {
                    latitude = location.getLatitude();
                    longitude = location.getLongitude();
                    Log.i("GPS_LOCATION", "Location: "+location.toString());
                    if(location.toString().contains("0.000000")) {
                        Log.i("Called", "Called inside");
                        isNetworkEnabled = true;
                        isGPSEnabled = false;
                        getLocation();
                    }
                }
            }
        } 
    }
    else if (isNetworkEnabled) {
        Log.d("Network", "Network");
        locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_TIME_BW_UPDATES, MIN_DISTANCE_CHANGE_FOR_UPDATES, this); 
        if (locationManager != null) {
            location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            if (location != null) {
                latitude = location.getLatitude();
                longitude = location.getLongitude();
                Log.i("NETWORK_LOCATION", "Location: "+location.toString());
            }
        }
    }
    else if (!isGPSEnabled && !isNetworkEnabled) {
        // no network provider is enabled and GPS is off.
        Log.d("NOT ENABLED", "Use WIFI");
    }
    return location;
}

What happened yesterday was that I went a few miles away from my home, got a wifi connection and had GPS enabled, but what happened was that although my location updated via Wifi, blue dot on map. It would not zoom into it. Since GPS was enabled (but couldn't get a fix) it went down that route and got LastKnownLocation() which was back at my house. Even though the blue dot was correct, it kept zooming to where I last was.

Is there a way I can have it so that it checks for GPS but doesn't use LastKnownLocation ? It instead defaults to a network check, then the network check will not use lastknownLocation , it will default to Wifi. Then Wifi can have LastKnownLocation if need be. Realistically I only want to get the lastKnownLocation at the Wifi stage as a last resort.

Hopefully someone can help me on this. I don't think it's as simple as removing lastKnownLocation from the code.

Thanks for any help you may provide.

There's all kinds of ways to do this. Here's a few tips:

  1. Don't use an old LastKnownLocation. Check for the age first!
  2. Location provides the Provider via getProvider() , check to see if you have your highest priority first, then work on a secondary.
  3. Use the GPS with the highest accuracy.

You might want to take a careful look at Location Strategies , specifically the isBetterLocation function. You probably want something like this, although you'll need to tweak it to suit your needs.

protected boolean isBetterLocation(Location location, Location currentBestLocation) {
    if (currentBestLocation == null) {
        // A new location is always better than no location
        return true;
    }

    // Check whether the new location fix is newer or older
    long timeDelta = location.getTime() - currentBestLocation.getTime();
    boolean isSignificantlyNewer = timeDelta > TWO_MINUTES;
    boolean isSignificantlyOlder = timeDelta < -TWO_MINUTES;
    boolean isNewer = timeDelta > 0;

    // If it's been more than two minutes since the current location, use the new location
    // because the user has likely moved
    if (isSignificantlyNewer) {
        return true;
    // If the new location is more than two minutes older, it must be worse
    } else if (isSignificantlyOlder) {
        return false;
    }

    // Check whether the new location fix is more or less accurate
    int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
    boolean isLessAccurate = accuracyDelta > 0;
    boolean isMoreAccurate = accuracyDelta < 0;
    boolean isSignificantlyLessAccurate = accuracyDelta > 200;

    // Check if the old and new location are from the same provider
    boolean isFromSameProvider = isSameProvider(location.getProvider(),
            currentBestLocation.getProvider());

    // Determine location quality using a combination of timeliness and accuracy
    if (isMoreAccurate) {
        return true;
    } else if (isNewer && !isLessAccurate) {
        return true;
    } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
        return true;
    }
    return false;
}

/** Checks whether two providers are the same */
private boolean isSameProvider(String provider1, String provider2) {
    if (provider1 == null) {
      return provider2 == null;
    }
    return provider1.equals(provider2);
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM