-1

I'm trying to get Location information using getLastKnownLocation but I often get null.

Here is what I do:

  1. Turn OFF location in Settings
  2. Run my app - getLastKnownLocation returns null - this is expected
  3. Turn ON location in Settings
  4. Run my app again - getLastKnownLocation returns null !?
  5. Run Google Maps
  6. Run my app again - getLastKnownLocation returns a valid Location !?

I found this question where the accepted answer suggests to launch Maps first in order to get the location: FusedLocationApi.getLastLocation always null

The questions is: what is Google Maps doing to get the location? And how can I do the same in my code?

Thanks!

Phantômaxx
  • 37,901
  • 21
  • 84
  • 115
Daniel
  • 304
  • 2
  • 12
  • https://developer.android.com/guide/topics/location/strategies.html – CommonsWare Mar 22 '19 at 12:50
  • 1
    You will need to request position updates at some point so that the device actually has a lastKnownLocation – JensV Mar 22 '19 at 12:51
  • https://developer.android.com/training/location/receive-location-updates – JensV Mar 22 '19 at 12:51
  • 2
    Use requestLocationUpdates. DO not rely on getLastKnownLocation, its an optimization AT BEST. Its almost better to forget it exists it works so infrequently. – Gabe Sechan Mar 22 '19 at 12:53
  • _"what is Google Maps doing to get the location?"_ It requests location updates. Android devices don't try to determine their location unless some app request the location to be determined. That explains why opening Google Maps suddenly made your app receive a valid location and it also explains why so many people wonder about `getLastKnownLocation()` returning `null`. – Markus Kauppinen Mar 22 '19 at 14:19
  • Thanks Markus! - that seems to be the main issue - **requesting updates** - and I was also missing the **uses-feature** declarations in manifest - that's often overlooked in other answers - thanks CommonsWare! – Daniel Mar 22 '19 at 16:01

3 Answers3

4

Thanks everyone for the help!

I was missing uses-features declarations in my manifest - thanks @CommonsWare for the link: https://developer.android.com/guide/topics/location/strategies.html#java

<!-- Needed only if your app targets Android 5.0 (API level 21) or higher. -->
<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.location.network" />

Also, requesting updates like this is needed as well:

locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

In my case the GPS provider needs a few minutes to connect and start sending data - the Network provider seems to be faster but does not always work - so I'll use both of them.

Update: a few key points

  1. It's not really intuitive, but in order to get data from getLastKnownLocation some app on the device - be it Google Maps or our own app - needs to call requestLocationUpdates.
  2. Some providers might not be enabled on a particular device - so it's a good idea to use both the GPS_PROVIDER and the NETWORK_PROVIDER - or use all enabled providers: locationManager.getProviders(true)

Here is the code I ended up using:

for ( String provider : locationManager.getProviders(true) ) {
    setLocation( locationManager.getLastKnownLocation(provider) );
    locationManager.requestLocationUpdates(provider, Tools.MIN_5, 0, locationListener);
}
// call setLocation again in locationListener.onLocationChanged
Daniel
  • 304
  • 2
  • 12
1

here is a code chunk which may help you.

  if (!::mFusedLocationProviderClient.isInitialized) {
        mFusedLocationProviderClient = FusedLocationProviderClient(mContext)
        mLocationRequest = LocationRequest()
        mLocationRequest.priority = LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY
        mLocationRequest.interval = 10000
        mLocationRequest.fastestInterval = 5000
        mLocationRequest.smallestDisplacement = 1000 * 100f
    }
    if (ActivityCompat.checkSelfPermission(
            AppName.appContext,
            android.Manifest.permission.ACCESS_FINE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(
            AppName.appContext,
            android.Manifest.permission.ACCESS_COARSE_LOCATION
        ) != PackageManager.PERMISSION_GRANTED
    ) {
        // not enough permission
        return
    }
    mFusedLocationProviderClient.requestLocationUpdates(mLocationRequest, object : LocationCallback() {
        override fun onLocationResult(p0: LocationResult?) {
            super.onLocationResult(p0)
            mFusedLocationProviderClient.removeLocationUpdates(this)
            if (p0 != null) {
             //here is your location
            } else {
              // location is null
            }
        }

        override fun onLocationAvailability(p0: LocationAvailability?) {
            if(p0?.isLocationAvailable!= true){
       //location not available.
            }
        }

    }, Looper.getMainLooper())
Amit Goswami
  • 314
  • 2
  • 11
0

make sure you give all permission and if you run app above 6.1 device then handle permission..

after that used this code..

add below dependency into app level gradle file..

//Place API
implementation 'com.google.android.gms:play-services-places:16.0.0'
implementation 'com.google.android.gms:play-services-location:16.0.0'

define this into class level as global deceleration.

private lateinit var fusedLocationClient: FusedLocationProviderClient
private var context: Context? = null
private var locationCallback: LocationCallback? = null
private var locationRequest: LocationRequest? = null
private var googleApiClient: GoogleApiClient? = null

after that onCreate method..

        fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)

make method for getting location..

fun getCurrentLocation() {
    // Get Current location and do reverse geocoding
    ProgressUtils.showOldProgressDialog(this)


    locationCallback = object : LocationCallback() {
        override fun onLocationResult(locationResult: LocationResult?) {
            locationResult ?: return
            ProgressUtils.closeOldProgressDialog()
            for (location in locationResult.locations) {
                // here you get current lat ,long,etc value.. 
                        stopLocationUpdates()
                    }
                } catch (e: Exception) {
                    e.message.loggerError()
                    stopLocationUpdates()
                }
            }
        }
    }

    locationRequest = LocationRequest().apply {
        interval = 10000
        fastestInterval = 5000
        priority = LocationRequest.PRIORITY_HIGH_ACCURACY
    }


    startLocationUpdates()


}

make location update..

 fun startLocationUpdates() {

    googleApiClient = GoogleApiClient.Builder(this)
            .addApi(LocationServices.API)
            .build()

    googleApiClient!!.connect()


    val builder = LocationSettingsRequest.Builder().addLocationRequest(locationRequest!!)
    builder.setAlwaysShow(true)

    val result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build())
    result.setResultCallback { result ->
        val status = result.status
        when (status.statusCode) {
            LocationSettingsStatusCodes.SUCCESS -> {
                Log.i(TAG, "All location settings are satisfied.")
                fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)
            }
            LocationSettingsStatusCodes.RESOLUTION_REQUIRED -> {
                Log.i(TAG, "Location settings are not satisfied. Show the user a dialog to upgrade location settings ")

                try {
                    // Show the dialog by calling startResolutionForResult(), and check the result
                    // in onActivityResult().
                    status.startResolutionForResult(this, LOCATION_SETTING_REQUEST_CODE)
                } catch (e: IntentSender.SendIntentException) {
                    Log.i(TAG, "PendingIntent unable to execute request.")
                }

            }
            LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE -> Log.i(TAG, "Location settings are inadequate, and cannot be fixed here. Dialog not created.")
        }
    }

}

make method for stop location..

private fun stopLocationUpdates() {

    if (locationCallback != null) {
        fusedLocationClient?.removeLocationUpdates(locationCallback)
    }
}

override fun onStop() {
    super.onStop()
    stopLocationUpdates()
}

override fun onPause() {
    super.onPause()
    stopLocationUpdates()
}

if handle permission than permission is grant then call back getting onActivity Result method there put below code..

     fusedLocationClient.requestLocationUpdates(locationRequest, locationCallback, null)