39

Android 4.3 on Moto G, Android 4.4.2 on Nexus 7 2012, Android 4.4.2 on Nexus 5. Android Studio 0.4.

I don't want to receive regular location updates, I just want an accurate location when the user presses a button.

I have followed this example: https://developer.android.com/training/location/retrieve-current.html

In manifest file:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

I check that Play Services are available using GooglePlayServicesUtil.isGooglePlayServicesAvailable

In main activity:

//in activity onCreate method
mLocationClient = new LocationClient(this, this, this);

@Override
protected void onStart() {
    mLocationClient.connect();
    super.onStart();
}

@Override
protected void onStop() {
    mLocationClient.disconnect();
    super.onStop();
}

//in button onclick method    
mCurrentLocation = mLocationClient.getLastLocation();

I have no SIM card. If I enable Wifi then sometimes I get an accurate location. Other times mCurrentLocation is null.

If I disable Wifi then mCurrentLocation is always null.

I am testing outside in several locations always with a clear view of the sky. I waited three minutes in each location.

I never see the GPS icon appear on the Android notification bar at the top of the screen.

I have these location settings: enter image description here

A GPS Test app manages to use GPS successfully indoors on the same device with Wi-Fi disabled so GPS is working: enter image description here

Registering for location updates, as at https://developer.android.com/training/location/receive-location-updates.html, doesn't work either. Registered method never called.

What am I doing wrong?

cja
  • 9,512
  • 21
  • 75
  • 129
  • 1
    post some code, it does take some time for GPS to get a proper lock too – tyczj Jan 09 '14 at 14:28
  • 1
    @tyczj Just edited question to add code. I waited three minutes in each location. Neither time did the GPS icon appear in the Android notification bar. – cja Jan 09 '14 at 14:31
  • Do you have another device to test on, to rule out device issues? – Sean Barbeau Jan 14 '14 at 16:18
  • I've tried also on Nexus 5 and Nexus 7 2012 with same results. Will update question to reflect this – cja Jan 14 '14 at 16:19
  • 1
    Thanks! Can you also post the LocationRequest code you're using to register for location updates? Also, I assume that when you say you are turning on WiFi you also have an internet connection? You can also test the fused provider using my GPS Benchmark app here - https://play.google.com/store/apps/details?id=com.gpsbenchmark.android – Sean Barbeau Jan 14 '14 at 16:24
  • 1
    GPS Benchmark looks like it'll be very useful – cja Jan 14 '14 at 16:34
  • 1
    can you please post your full code of requesting location updates like you said you would – tyczj Jan 14 '14 at 18:56
  • 3
    do you really need both permissions? – Marian Paździoch Dec 12 '14 at 15:20
  • Is project on image open source? I'm trying to implement a radar view displaying satellites used or on view. What defines their position on radar? I suppose you define their colors based on SNR and shapers are based on used(circles are used, triangles are not), right? Azimuth defines their position, is distance to center based on elevation? – Thracian Oct 10 '17 at 16:34
  • The satellite screenshot is from an app I downloaded. I don't remember its name. – cja Oct 10 '17 at 16:39

12 Answers12

16

I solved it. The problem was that "Let Google apps access your location" was turned off: enter image description here

When I turn it on I get GPS readings and when it's off I don't.

I had left it off for two reasons:

  1. I'm developing an app to be used to lots of devices at a company and I want minimum manual configuration to be necessary

  2. The screen says clearly "This setting affects Google apps only." I know that Play Services is Google software but I didn't think Google would expect an end user to understand that.

Then I got the Android 4.4.2 update and the location settings page has changed. It appears that I can have Google Location Reporting turned off and still get GPS readings from the fused location provider: enter image description here

So maybe Google realised that the setting was confusing and improved it. Either way, I'd have saved a lot of time if I'd got 4.4.2 a few days ago.

cja
  • 9,512
  • 21
  • 75
  • 129
  • You've all been nice and helpful but I hope there's a way I can award myself the reputation bounty! – cja Jan 15 '14 at 17:06
  • 1
    For those who find this thread now - Bad news: Google made it harder to understand. Now there is no explicit "google location settings". If you choose "high accuracy" or "battery saving" location mode, you are accepting Google location settings. Good news: you can use SettingsApi and don't have to redirect users to mysterious device setting view https://coderwall.com/p/zvmefa/fusedlocationproviderapi-doesn-t-work-without-network-location-provider-use-settingsapi-for-checking-and-setting-up-user-s-configuration?p=1&q=author%3Adaisy1754 – Kazuki May 16 '15 at 22:00
15

The problem is with getLastLocation() because it uses a cached location. I had the same problem as I also tried to use this simple approach. Since, I have switched to listening to updates (and stopping after 1st successfull update automatically).

This is my code that works.

First, the check for availability in Application (not essential, can be in Activity and without keeping of result):

public class MainApp extends Application {
  public static enum PlayServices {
    NOT_CHECKED, AVAILABLE, UNAVAILABLE
  };
  public static PlayServices mPlayServices = PlayServices.NOT_CHECKED;

  @Override
  public void onCreate() {
    super.onCreate();

    if (GooglePlayServicesUtil.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS) {
      MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;
    }
  }
}

Then, on to the Activity:

public class MainActivity extends SherlockFragmentActivity implements
  GooglePlayServicesClient.ConnectionCallbacks,
  GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {

In its onCreate():

if (MainApp.mPlayServices != MainApp.PlayServices.UNAVAILABLE) {
  mLocationClient = new LocationClient(this, this, this);

  mLocationRequest = LocationRequest.create();
  mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
  mLocationRequest.setInterval(5000);
  mLocationRequest.setNumUpdates(1);
  mLocationRequest.setFastestInterval(1000);

  mUpdatesRequested = false;
  MainApp.prefs.edit().putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested)
      .commit();
}

The rest of the MainActivity class:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  Log.d(this.getClass().getSimpleName(), "onActivityResult(" + requestCode + ", " + resultCode
      + ")");
  // Decide what to do based on the original request code
  switch (requestCode) {
    case MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST:
      /*
       * If the result code is Activity.RESULT_OK, try
       * to connect again
       */
      switch (resultCode) {
        case Activity.RESULT_OK:
          // here we want to initiate location requests!
          mLocationClient = new LocationClient(this, this, this);

          break;
      }
      break;
  }
}

@Override
public void onConnected(Bundle dataBundle) {
  Log.d(this.getClass().getSimpleName(), "onConnected()");

  Log.d(this.getClass().getSimpleName(), "Google Play Services are available.");
  MainApp.mPlayServices = MainApp.PlayServices.AVAILABLE;

  if (!mUpdatesRequested) {

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

    boolean gps_enabled = false;
    try {
      gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
    } catch (Exception ex) {
    }

    boolean network_enabled = false;
    try {
      network_enabled = lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
    } catch (Exception ex) {
    }

    // don't start listeners if no provider is enabled
    MainApp.locEnabled = gps_enabled || network_enabled;

    if (!MainApp.locEnabled) {
      // we have access to PlayServices, but user has disabled location visibility --> alert him
      alertLocationOff();
    } else {
      mLocationClient.requestLocationUpdates(mLocationRequest, this);
      mUpdatesRequested = true;
    }
  }
}

@Override
public void onDisconnected() {
  Log.d(this.getClass().getSimpleName(), "onDisconnected()");
}

@Override
public void onConnectionFailed(ConnectionResult connectionResult) {
  Log.d(this.getClass().getSimpleName(), "onConnectionFailed()");

  Log.d(this.getClass().getSimpleName(), "Google Play Services not available.");
  MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;

  /*
   * Google Play services can resolve some errors it detects.
   * If the error has a resolution, try sending an Intent to
   * start a Google Play services activity that can resolve
   * error.
   */
  if (connectionResult.hasResolution()) {
    try {
      // Start an Activity that tries to resolve the error
      connectionResult.startResolutionForResult(this,
          MainApp.PLAY_CONNECTION_FAILURE_RESOLUTION_REQUEST);
      /*
       * Thrown if Google Play services canceled the original
       * PendingIntent
       */
    } catch (IntentSender.SendIntentException e) {
      // Log the error
      e.printStackTrace();
    }
  } else {
    /*
     * If no resolution is available, display a dialog to the
     * user with the error.
     */
    GooglePlayServicesUtil.getErrorDialog(connectionResult.getErrorCode(), this, 0).show();
  }
}

@SuppressLint("NewApi")
@Override
public void onLocationChanged(Location location) {
  Log.d(this.getClass().getSimpleName(), "onLocationChanged(), location=" + location);

  if (location != null) {
    boolean present = true;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.GINGERBREAD) {
      present = Geocoder.isPresent();
    }

    if (present) {
      (new ExtractLocationTask(this)).execute(location);
    } else {
      Log.e(this.getClass().getSimpleName(), "Geocoder not present");
      MainApp.mPlayServices = MainApp.PlayServices.UNAVAILABLE;
    }
  }
}


private class ExtractLocationTask extends AsyncTask<Location, Void, Boolean> {
  Context mContext;

  public ExtractLocationTask(Context context) {
    super();
    mContext = context;
  }

  @Override
  protected Boolean doInBackground(Location... params) {
    Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPreExecute()");

    boolean found = false;
    try {
      Geocoder geoCoder_local = new Geocoder(mContext, Locale.getDefault());
      Geocoder geoCoder_en = new Geocoder(mContext, Locale.ENGLISH);

      List<Address> addresses_local = geoCoder_local.getFromLocation(params[0].getLatitude(),
          params[0].getLongitude(), 10);
      List<Address> addresses_en = geoCoder_en.getFromLocation(params[0].getLatitude(),
          params[0].getLongitude(), 10);

      if (addresses_local != null && addresses_local.size() > 0) {

        // do what you want with location info here

        // based on mLocationRequest.setNumUpdates(1), no need to call
        // removeLocationUpdates()

        MainApp.locEnabled = true;

        mUpdatesRequested = false;
        MainApp.prefs.edit()
            .putBoolean(MainApp.KEY_LOCATION_UPDATES_REQUESTED, mUpdatesRequested).commit();

        found = true;
      }
    } catch (IOException e) {
      Log.e(this.getClass().getSimpleName(), "Exception: ", e);
    }

    return found;
  }

  @Override
  protected void onPostExecute(Boolean found) {
    Log.d(getClass().getSimpleName(), "ExtractLocationTask.onPostExecute()");

    if (found) {
      // update UI etc.
    } else if (!mUpdatesReRequested) {
      mLocationClient.requestLocationUpdates(mLocationRequest, (LocationListener) mContext);
      mUpdatesRequested = true;
      mUpdatesReRequested = true;
    }
  }
}

I hope this will help you get it to work!

Saran
  • 3,845
  • 3
  • 37
  • 59
  • 3
    Does LocationManager.isProviderEnabled work with the fused location provider? – cja Jan 14 '14 at 14:44
  • I found it to work w/o an issue, but you can easily just remove the LocationManager checks from the `onConnected()` and just use `mLocationClient.requestLocationUpdates(mLocationRequest, this);` – Saran Jan 14 '14 at 14:53
  • 8
    Btw. from **FusedLocation source code** (https://android.googlesource.com/platform/frameworks/base/+/6fa9ad4afcd762aea519ff61811386c23d18ddb2/packages/FusedLocation/src/com/android/location/fused/FusionEngine.java) you can see they are using the LocationManager in the same way (with `isProviderEnabled()`) to check the availability of GPS & NETWORK. – Saran Jan 14 '14 at 14:58
  • 2
    Thanks for confirming the LocationManager usage – cja Jan 14 '14 at 14:59
  • 3
    There are still so many problem with Google Play Location Services. Sometimes it does not detect location and taking to long time. However, Old technique provide you quick location than Google Play Location Services API. If anyone wants to experience this issue, just observed that whenever your MAP application showing "Waiting for Location" at the same time run your sample app using old location technique. Old one return very fast result that Play Location Service. – Sachin Shelke Feb 08 '14 at 17:16
  • You should add "else MyApplication.locEnabled = false;" – Rony Tesler Oct 13 '14 at 20:33
  • Could you please clarify the variables / Show their declaration? MainApp, mLocationRequest, mLocationClient, etc. Thanks. – FlorianB Sep 01 '16 at 14:07
5

Location provider won't wake up GPS until some of clients ask (subscribe) for high precision location (as described in examples given by other users). GPS test app doesn't use location provider but uses old "direct" way of obtaining location.

Also there is expiry mechanism, which removes information about last location after some time if it is believed to be stale.

Summing up all above it is really possible that LP(Location Provider) has nothing to give you.

Narendra Baratam
  • 828
  • 1
  • 12
  • 26
Andrew
  • 1,756
  • 3
  • 18
  • 31
2

I think your are testing your application in Indoor , it doesn't works..

and code flow..

public void setUpLocationClientIfNeeded() {
        createLocReq();
        if (mLocationClient == null) {
            mLocationClient = new LocationClient(getApplicationContext(), this, // ConnectionCallbacks
                    this); // OnConnectionFailedListener
        }
    }

    private void createLocReq() {
        if (mLocationRequest == null) {
            // Create a new global location parameters object
            mLocationRequest = LocationRequest.create();
            // Set the update interval
            mLocationRequest.setInterval(LocationServices.UPDATE_INTERVAL_IN_MILLISECONDS);
            // Use high accuracy
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            // Set the interval ceiling to one minute
            mLocationRequest.setFastestInterval(LocationServices.FAST_INTERVAL_CEILING_IN_MILLISECONDS);

            mLocationClient = new LocationClient(getApplicationContext(), this, this);
            mLocationClient.connect();
        }
    }

    public void updateLocation(Location location) {
        if (lastTrackedLat == 0.0) {
            lastTrackedLat = location.getLatitude();
        }
        if (lastTrackedLng == 0.0) {
            lastTrackedLng = location.getLongitude();
        }

        currentLat = location.getLatitude();
        currentLng = location.getLongitude();
    }

    @Override
    public void onLocationChanged(Location location) {
        if (location != null) {
            this.location = location;
            updateLocation(location);
        }
    }

    public Location getLocation() {
        // mLocationClient.getLastLocation();
        return location;
    }
vinay Maneti
  • 1,447
  • 1
  • 23
  • 31
  • Thank you for your code example. However, I don't want to receive regular updates, I just want an accurate location when the user presses a button. I a following https://developer.android.com/training/location/retrieve-current.html – cja Jan 09 '14 at 14:08
  • Why not use FusedLocationProvider? – IgorGanapolsky Apr 07 '15 at 15:21
  • 1
    Now you can use FusedLocation Provider, it is better then the previous LocationManager. – vinay Maneti Apr 07 '15 at 15:27
1

According to the docs

This method provides a simplified way to get location. It is particularly well suited for applications that do not require an accurate location and that do not want to maintain extra logic for location updates.

so it may or may not return a highly accurate location.

GPS can take a while to lock on so calling getLastLocation may or may not return a location

you are better off requesting location updates then after you get a location just stop requesting location updates.

Also looking at you code you provided are you waiting for the LocationClient to connect before trying to get a location? That will certainly give you a null location since it is not connected to get the location yet.

what you should be doing is in your onConnected get the last location there, example

public void onConnected(Bundle connectionHint) {
    Location location = mLocationClient.getLastLocation();
}

as it says in that example onConnected is Called by Location Services when the request to connect the client finishes successfully. At this point, you can request the current location or start periodic updates

tyczj
  • 71,600
  • 54
  • 194
  • 296
  • 2
    If it could get a network location then it might give me that without using GPS but if it *can't* get a network location then surely it will use GPS. I can't believe that it would be designed to not even try GPS. I'm sure I'm doing something wrong. – cja Jan 09 '14 at 15:23
  • To reiterate, it isn't giving me an inaccurate location, it's giving me a null location. – cja Jan 09 '14 at 15:24
  • have you tried requesting location updates just to see how it differs and what happens? – tyczj Jan 09 '14 at 15:27
  • 2
    From http://developer.android.com/google/play-services/location.html: "Immediately available: Gives your apps immediate access to the best, most recent location." Either this is a lie or I'm doing something wrong. – cja Jan 10 '14 at 11:59
  • 1
    Registering for location updates, as at https://developer.android.com/training/location/receive-location-updates.html, doesn't work either. Registered method never called. – cja Jan 14 '14 at 12:26
  • @Saran Now I see why you commented - you had the most votes. I can see that your answer may be helpful to people with a variety of problems but it didn't help me with mine. – cja Jan 21 '14 at 15:46
  • 1
    The person with the most up votes definitely deserved the bounty. @cja – BenjaminPaul Apr 25 '14 at 13:05
  • @BenjaminPaul even if it didn't help me answer the question? – cja Apr 25 '14 at 13:08
  • @BenjaminPaul Well now that would be me anyway :) – cja Jul 11 '17 at 13:35
1

Without a sim card the coarse location provider has no way to know the coarse position, unless it is able to find a WiFi network that has been mapped by Google.

Requesting the last known location may result in a outdated location, and as such is rather useless. I guess this is the position that was recorded the last time some app requested a location update.

I use the following code to get a recent location:

    Criteria criteria = new Criteria();
    criteria.setAccuracy(Criteria.ACCURACY_COARSE);
    criteria.setAltitudeRequired(false);
    criteria.setSpeedRequired(false);
    criteria.setBearingRequired(false);
    criteria.setCostAllowed(false);

    final LocationManager manager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);

    ....
    LocationListener listener = new LocationListener() {
        @Override
        public void onLocationChanged(Location lastKnownLocation) {
                     ....
        }
        // rest of interface
     }


     manager.requestSingleUpdate(criteria, listener, null);

The last call ensures that we request the current location, not the location it was able to find an unknown amount of time before.

You might try to change it to Criteria.ACCURACY_FINE in order to get the GPS fired up. Be aware that if the GPS didn't have a fix for quite some while it may take more than several minutes before it is actually capable of getting a fix. I'd expect in the mean time that you'd see the GPS icon indicating it is waiting for a fix.

Narendra Baratam
  • 828
  • 1
  • 12
  • 26
M. le Rutte
  • 3,525
  • 3
  • 18
  • 31
  • "The last call" - which line are you referring to? – cja Jan 14 '14 at 13:04
  • Seems like `manager.requestSingleUpdate(criteria, listener, null);` does that exactly – meeDamian Jan 14 '14 at 13:16
  • "The last call" is the `requestSingleUpdate()` call. This will refresh the current location and once established be passed to the listener. After that there will be no more updates. – M. le Rutte Jan 14 '14 at 13:45
  • 4
    Hang on, this question is about the fused location provider. Your answer seems to use android.location.LocationManager, which is different. – cja Jan 14 '14 at 13:46
  • @user1993392 Why are demonstrating with LocationManager? Isn't FusedLocationProvider more preferred nowadays? – IgorGanapolsky Mar 06 '15 at 17:04
1

What's in the OnConnected method?

In this method you should create the LocationRequest object with PRIORITY_HIGH_ACCURACY priority.

@Override
public void onConnected(Bundle dataBundle) {
    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationClient.requestLocationUpdates(mLocationRequest, fusedListener);
}
Ahmed Salman Tahir
  • 1,783
  • 1
  • 17
  • 26
dan_flo10
  • 312
  • 2
  • 10
1

All you need to do is to add a priority property to the request object like this.

public void onConnected(Bundle arg0)
{
    locationrequest = LocationRequest.create();
    locationrequest.setInterval(1000);
    locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationclient.requestLocationUpdates(locationrequest, this);
}

Maybe use a boolean variable to let the user have options to force GPS like

boolean forceGPS=false;
.
.
.//make the user choose to change the value of the boolean
.
.
public void onConnected(Bundle arg0)
    {
        locationrequest = LocationRequest.create();
        locationrequest.setInterval(1000);
        if(forceGPS==true)
        {
        locationrequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        }
        locationclient.requestLocationUpdates(locationrequest, this);
    }
Sujal Mandal
  • 975
  • 1
  • 14
  • 29
0

Try this:

public final static int MINACCURACY = 50;

private LocationManager lm;
private LocationListener listener;

private void foundLoc(double x, double y) {
    // do something with x,y
    Log.v("DEBUG", x + "," + y);
}


public void findMe(View v) {
    lm = (LocationManager) getSystemService(LOCATION_SERVICE);
    listener = new LocationListener() {
        @Override
        public void onLocationChanged(Location location) {
            if(location.getAccuracy() < MINACCURACY) {
                foundLoc(location.getLatitude(), location.getLongitude());
                lm.removeUpdates(listener);
            }
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }

        @Override
        public void onProviderEnabled(String provider) {
        }

        @Override
        public void onProviderDisabled(String provider) {
        }
    };
    lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, 100, 0, listener);
}

MINACCURACY is in meters. This way, on button press (which calls the findMe method), the GPS is enabled, your location is found with a minimum accuracy, the method foundLoc gets the data and the listener terminates (which, in turn, disables GPS).

c0m3tx
  • 98
  • 1
  • 7
  • 1
    This question is about the fused location provider. Your answer seems to use android.location.LocationManager, which is different. – cja Jan 14 '14 at 14:09
0

I prefer to use Service that implements LocationListener to get current near-accurate location. I normally take following steps:

  1. Initialize LocationManager and call requestLocationUpdates for both GPS_PROVIDER and NETWORK_PROVIDER in onCreate()
  2. Call getLastKnownLocation for both GPS_PROVIDER and NETWORK_PROVIDER and use getTime() to know last newer location.
  3. Broadcast last location (if it's <30 sec old) (optional)
  4. In the meantime, when onLocationChanged is called I compare newly updated location with last best location (if any) and check accuracy level.
  5. If accuracy delta < MIN_ACCURACY (user variable), use it as best location
  6. Broadcast current best location
  7. Must remove LocationManager locationManager.removeUpdates(this);
  8. Call stopSelf(); (you can also stop service from activity's onDestroy)

EDIT:

OK...Above method was using Location API, below I have coded using Fused Provider (GooglePlayServices). I have tested on my Nexus 5 (Android 4.4.2), NO SIM, NO WIFI...and I'm getting results.

Edit 2: I have also tested on Android 2.3.3 LG Optimus (No SIM & Wifi) and it took about 5 min to get lock (using Fused Provided), but I was getting location icon instantly.

public class HomeActivity extends FragmentActivity implements
        ActionBar.OnNavigationListener,
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {

    private LocationClient locationClient;
    private LocationRequest locationRequest;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_home);
            // ...
            int resp = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
        if (resp == ConnectionResult.SUCCESS) {
            locationClient = new LocationClient(this, this, this);
            locationClient.connect();
        } else {
            Toast.makeText(this, "Google Play Service Error " + resp,
                    Toast.LENGTH_LONG).show();

        }
    }

    @Override
    public void onConnected(Bundle connectionHint) {
        if (locationClient != null && locationClient.isConnected()) {

            locationRequest = LocationRequest.create();
            locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
            locationRequest.setInterval(100);
            locationClient.requestLocationUpdates(locationRequest, this);

        }
    }

    @Override
    public void onLocationChanged(Location location) {

        try {
            if (location != null) {
                LatLng gps = new LatLng(location.getLatitude(), location.getLongitude());
                Log.v("JD", "My Location: " + gps.toString());
            }
        } catch (Exception e) {
            Log.d("JD",
                    "onLocationChanged", e);

        }       
    }
}
Tamal
  • 107
  • 1
  • 3
  • 3
    this has nothing to do with the fused location API in google play services – tyczj Jan 14 '14 at 14:24
  • Above method worked for my app with NO SIM and NO Wifi, which I thought may be more useful to OP. Anyway, I'll check n edit my post using Fused provider in a moment. – Tamal Jan 14 '14 at 14:57
  • above code is not using fused lcoation provider,by any chance can we get location from fused provider without turning on location in settings. – Srishti Roy Feb 04 '16 at 09:10
0

There are two things going on here that are causing you to sometimes get null, and sometimes get a location.

First, you're creating a new instance of the LocationClient in the onClick method, which is not the same instance you're calling connect() on in onStart(). This will create unpredictable behavior where sometimes the client will have enough time to connect before returning a result from LocationClient.getLastLocation(), and sometimes it won't.

Second, you should guard the call to LocationClient.getLastLocation() with a call to LocationClient.isConnected(). It says the following in the LocationClient.isConnected() docs: https://developer.android.com/reference/com/google/android/gms/location/LocationClient.html#isConnected()

Checks if the client is currently connected to the service, so that requests to other methods will succeed. Applications should guard client actions caused by the user with a call to this method.

Since the user is triggering the onClick() code by tapping on the button, you should call this method before trying to get the last location.

So, your code should look like this:

LocationClient mLocationClient;
Location mCurrentLocation;

@Override
protected void onCreate() {
    ...
    mLocationClient = new LocationClient(this, this, this);
    ...
}

@Override
protected void onStart() {
    mLocationClient.connect();
    super.onStart();
}

@Override
protected void onStop() {
    mLocationClient.disconnect();
    super.onStop();
}

public void onClick(View v) {
    ...
    if (mLocationClient.isConnected()) {
        // You should get a valid location here
        mCurrentLocation = mLocationClient.getLastLocation();
    }
}

This should give the LocationClient a long enough time to connect and give you a valid location, which should hopefully include GPS data.

Sean Barbeau
  • 11,496
  • 8
  • 58
  • 111
0

Since no one has shared this link, I found this to be the most helpful documentation http://developer.android.com/guide/topics/location/strategies.html

"You might expect that the most recent location fix is the most accurate. However, because the accuracy of a location fix varies, the most recent fix is not always the best. You should include logic for choosing location fixes based on several criteria. The criteria also varies depending on the use-cases of the application and field testing."

Your best bet is to keep a location listener going as long as the activity is in the foreground and select the most accurate cached location when the button is pressed. You may need to show a spinner or something and disable the button while it waits for an accurate measurement to show up.

Eric Woodruff
  • 6,380
  • 3
  • 36
  • 33