37

Is there a way to access the GPS once instead of having a looper that constantly checks for location updates?

In my scenario all I'm interested in is finding the current co-ordinates and not a continuous connection with the GPS satellite. Does anyone have any ideas how this can be done? Thanks in advance.

progdoc
  • 579
  • 3
  • 11
  • 24

2 Answers2

40

Dont use the getLastKnownLocation because that could be returning null or old data.

This code Only fetches the location once a button is pressed and not every time. People use to leave the location listener listen in every instance and that kills the battery life so Use the code snippet I have posted by doing lots of research:

// get the text view and buttons from the xml layout
Button button = (Button) findViewById(R.id.btnGetLocation);
final TextView latitude = (TextView) findViewById(R.id.textview4);
final TextView longitude = (TextView) findViewById(R.id.textview5);
final LocationListener locationListener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            mlocation = location;
            Log.d("Location Changes", location.toString());
            latitude.setText(String.valueOf(location.getLatitude()));
            longitude.setText(String.valueOf(location.getLongitude()));
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            Log.d("Status Changed", String.valueOf(status));
        }

        @Override
        public void onProviderEnabled(String provider) {
            Log.d("Provider Enabled", provider);
        }

        @Override
        public void onProviderDisabled(String provider) {
            Log.d("Provider Disabled", provider);
        }
    };

    // Now first make a criteria with your requirements
    // this is done to save the battery life of the device
    // there are various other other criteria you can search for..
    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_COARSE);
    criteria.setPowerRequirement(Criteria.POWER_LOW);
    criteria.setAltitudeRequired(false);
    criteria.setBearingRequired(false);
    criteria.setSpeedRequired(false);
    criteria.setCostAllowed(true);
    criteria.setHorizontalAccuracy(Criteria.ACCURACY_HIGH);
    criteria.setVerticalAccuracy(Criteria.ACCURACY_HIGH);

    // Now create a location manager
    final LocationManager locationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

   // This is the Best And IMPORTANT part
    final Looper looper = null;

   // Now whenever the button is clicked fetch the location one time
   button.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            locationManager.requestSingleUpdate(criteria, locationListener, looper);
       }
   });
seekingStillness
  • 3,967
  • 3
  • 25
  • 52
msmukesh4
  • 569
  • 4
  • 7
  • 2
    yeh thats great – Udara Kasun Mar 29 '19 at 16:52
  • 1
    @msmukesh4 Just to be clear, This location listener will work only once, right? I mean.... If I click on the button once, it will give a single update and won't update after that even if the location changes, right? – Mohd Naved Nov 14 '19 at 05:21
  • 1
    That is a really great answer. Thank you very much. – user1222936 Dec 06 '19 at 11:38
  • 1
    Dude, you save my life. I dont know how this answer is not the accepted. thanks a lot! – Alonso Ato Neyra Feb 27 '20 at 00:45
  • @msmukesh4 great answer, but this **freezes the UI** (too much work on main thread) and with async call not able to get location update. – Deepak Kumar May 24 '20 at 07:56
  • @deepakkumar, in what conditions does it freeze UI? Slow GPS? – CoolMind Jun 16 '20 at 14:17
  • Thanks! Why `setCostAllowed(true)`? I think, we should add `locationListener?.let { locationManager?.removeUpdates(it) }` in `onDestroy`. Also it is very strange that inside `onLocationChanged` event we cannot update Fragment's view (change `TextView`s, colors, etc.). – CoolMind Jun 18 '20 at 15:21
  • The important part is `horizontalAccuracy = Criteria.ACCURACY_HIGH; verticalAccuracy = Criteria.ACCURACY_HIGH`. Without it sometimes `onLocationChanged` is not called. Instead of `looper` simply set `null`. See also https://stackoverflow.com/a/34626714/2914140. In API 30 this method is deprecated, but a new one (`getCurrentLocation`) is still not resolved. – CoolMind Jun 19 '20 at 08:59
39

First check if the last know location is recent. If not, I believe you must to set up onLocationChanged listener, but once you get your first valid location you can always stop the stream of updates.

Addition

public class Example extends Activity implements LocationListener {
    LocationManager mLocationManager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        Location location = mLocationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
        if(location != null && location.getTime() > Calendar.getInstance().getTimeInMillis() - 2 * 60 * 1000) {
            // Do something with the recent location fix
            //  otherwise wait for the update below
        }
        else {
            mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
        }
    }

    public void onLocationChanged(Location location) {
        if (location != null) {
            Log.v("Location Changed", location.getLatitude() + " and " + location.getLongitude());
            mLocationManager.removeUpdates(this);
        }
    }

    // Required functions    
    public void onProviderDisabled(String arg0) {}
    public void onProviderEnabled(String arg0) {}
    public void onStatusChanged(String arg0, int arg1, Bundle arg2) {}
}
Sam
  • 84,460
  • 18
  • 172
  • 171
  • ye thats precisely my question, i set up an onLocationChanged and I would like to just get one valid location..how can I stop the stream of updates? Is there some command? Thanks – progdoc May 09 '12 at 21:39
  • Short answer LocationManager.removeUpdates(). Long answer, see my update. – Sam May 09 '12 at 21:42
  • what does this `location.getTime() > Calendar.getInstance().getTimeInMillis() - 2 * 60 * 1000` mean in above if condition? – Om3ga Jun 14 '12 at 09:55
  • 2
    @2619 As the time is measured in milliseconds, multiplying by 1000 gives you seconds, multiplying again by 60 gives you 1 minute, and multiplying by 2 again gives you the total time of 2 minutes. The if statement therefore checks if the location was received more than 2 minutes ago. – Ymabob Sep 14 '14 at 18:59
  • A good way but what I noticed during my own project is that if the last known location has not changed (even though it is stale in terms of time), then `onLocationChanged` event will not fire. Thus if you only depend on this call for whatever you do, it will not be reached since event is not fired. – mcy Mar 07 '17 at 12:27