The fused Location Provider will only maintain background location if at least one client is connected to it. Now just turning on the location service will not guarantee to store the last known location.
Once the first client connects, it will immediately try to get a location. If your activity is the first client to connect and getLastLocation() is invoked right away in onConnected(), that might not be enough time for the first location to arrive..
I suggest you to launch the Maps app first, so that there is at least some confirmed location, and then test your app.
Answer from Amit K. Saha on Stack OverflowThe fused Location Provider will only maintain background location if at least one client is connected to it. Now just turning on the location service will not guarantee to store the last known location.
Once the first client connects, it will immediately try to get a location. If your activity is the first client to connect and getLastLocation() is invoked right away in onConnected(), that might not be enough time for the first location to arrive..
I suggest you to launch the Maps app first, so that there is at least some confirmed location, and then test your app.
As in this post said, The fused Location Provider will only maintain background location if at least one client is connected to it.
But we can skip the process of launching the Google Maps app to get last location by following way.
What we need to do is
- We have to request location update from FusedLocationProviderClient
- Then we can get the last location from FusedLocationProviderClient, it wouldn't be null.
Request Location
LocationRequest mLocationRequest = LocationRequest.create();
mLocationRequest.setInterval(60000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationCallback mLocationCallback = new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
if (locationResult == null) {
return;
}
for (Location location : locationResult.getLocations()) {
if (location != null) {
//TODO: UI updates.
}
}
}
};
LocationServices.getFusedLocationProviderClient(context).requestLocationUpdates(mLocationRequest, mLocationCallback, null);
Get last location
LocationServices.getFusedLocationProviderClient(context).getLastLocation().addOnSuccessListener(new OnSuccessListener<Location>() {
@Override
public void onSuccess(Location location) {
//TODO: UI updates.
}
});
For best result requestLocationUpdates in onStart() of the Activity, and then you can get last location.
There's nothing to fix, this is expected behavior. getLastLocation returns the last location IF THE SYSTEM KNOWS IT. It almost never knows it. So it will almost always return null. If you want to get an assured location, use requestLocationUpdates. But you can never rely on getLastLocation working, and even when it does the data may be old. It should really only be used in very limited circumstances.
The getLastLocation() returns null if the Android System doesn't have a knowledge about its last location.To provide last location to the System you can try one of the following things:
Before opening your app, open Google Maps and let the device get the current location.After that you can return to your app.
The
Locationservice inAndroidprovide 3 modes.Check-up the High Accuracy mode. Go to Setting -> Location -> High Accuracy Mode.
I have faced this isue as well especially when i first install my app on a device. there are two things that you can try out that i think will help:
one is trying to open the google maps app and waiting for it to get a fix and then go on and open your app. this should provide your fused location provider client a last location that it can look up. check this answer for reference
the second is going a step further and requesting location updates using the provider. If you only need a single location fix you can then go on and deregister it once you get the first one. I am doing this for my app and usually last location is only null the first time i use the app after installation. after that it usually works. this is probably what you should do anyway as you cant expect your users to turn on google maps before using your app.
and just to be safe, be aware that fusedlocationproviderclient sometimes stops working or behaves weirdly when you have pending google services updates on your device. so go ahead and make sure your device is up to date in this regard.
lastLocation can be null for several reasons. They are listed here https://developer.android.com/training/location/retrieve-current#last-known
I found a solution, this is what happens, when the location is null it means the location cache was cleared, this happens when turning off GPS, so when I was turning it on there was no last location to get, what I did was this:
CopycheckLocationSettings(callingActivity, turnOnGpsRequestCode, callback) {
// Location settings successful
mFusedLocationProviderClient!!.lastLocation
.addOnSuccessListener(callingActivity) {
location ->
if (location == null || location.accuracy > 100) {
mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
stopLocationUpdates()
if (locationResult != null && locationResult.locations.isNotEmpty()) {
val newLocation = locationResult.locations[0]
callback.onCallback(Status.SUCCESS, newLocation)
} else {
callback.onCallback(Status.ERROR_LOCATION, null)
}
}
}
mFusedLocationProviderClient!!.requestLocationUpdates(getLocationRequest(),
mLocationCallback, null)
} else {
callback.onCallback(Status.SUCCESS, location)
}
}
.addOnFailureListener {
callback.onCallback(Status.ERROR_UNKNOWN, null)
}
}
When the location is null, start requesting locations using a callback and
mFusedLocationProviderClient!!.requestLocationUpdates(getLocationRequest(), mLocationCallback, null)
Then when the callback is called, a new location is got and it starts getting location again.
Sometimes it happens that when you turn on the GPS, the location is not null but the accuracy is bad, so I also check if location accuracy is good enough (For me good enough is 100 meters)
You can use getLocationAvailability() method on your FusedLocationPrivedClient object and if it returns true then only use getLastLocation() method otherwise use requestLocationUpdates() method like this :
CopyFusedLocationProviderClient fusedLocationProviderClient =
LocationServices.getFusedLocationProviderClient(InitActivity.this);
if (ActivityCompat.checkSelfPermission(this.getApplicationContext(),
android.Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
fusedLocationProviderClient.getLocationAvailability().addOnSuccessListener(new OnSuccessListener<LocationAvailability>() {
@Override
public void onSuccess(LocationAvailability locationAvailability) {
Log.d(TAG, "onSuccess: locationAvailability.isLocationAvailable " + locationAvailability.isLocationAvailable());
if (locationAvailability.isLocationAvailable()) {
if (ActivityCompat.checkSelfPermission(InitActivity.this.getApplicationContext(),
android.Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED) {
Task<Location> locationTask = fusedLocationProviderClient.getLastLocation();
locationTask.addOnCompleteListener(new OnCompleteListener<Location>() {
@Override
public void onComplete(@NonNull Task<Location> task) {
Location location = task.getResult();
}
});
} else {
requestLocationPermissions ();
}
} else {
fusedLocationProviderClient.requestLocationUpdates(locationRequest, pendingIntent);
}
}
});
} else {
requestLocationPermissions ();
}
Instead of getting the last known location, you can get location updates in specific intervals. Here is the code.
Declare these fused location provider class and location callback class
private var mFusedLocationProviderClient: FusedLocationProviderClient? = null
private var mLocationRequest: LocationRequest? = null
private var mLocationCallback: LocationCallback? = null
private const val LOCATION_REQUEST_INTERVAL: Long = 5000
`
Here is the necessary method for the location request
private fun createLocationRequest() {
mLocationRequest = LocationRequest.create()
mLocationRequest!!.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
mLocationRequest!!.setInterval(LOCATION_REQUEST_INTERVAL).fastestInterval =
LOCATION_REQUEST_INTERVAL
requestLocationUpdate()
}
private fun requestLocationUpdate() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED ) {
Log.e("permission", "denied");
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return
}
mFusedLocationProviderClient!!.requestLocationUpdates(
mLocationRequest,
mLocationCallback,
Looper.myLooper()
)
}
Then call these "createLocationRequest" method and location callback classs in onCreate().
mFusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(requireContext())
createLocationRequest()
mLocationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult) {
super.onLocationResult(locationResult)
val lat = locationResult.lastLocation.latitude
val lng = locationResult.lastLocation.longitude
}
}
And remove location update listener in onPause() or onDestory() as per your requirement.
mFusedLocationProviderClient!!.removeLocationUpdates(mLocationCallback)
Scenario 1:After gets message that location is disabled and exits.kill the app from recent app tray now Enable location . Then open App from Home page as new Activity now it should update location. if it's working,nothing wrong in App.App is working properly.
Scenario 2:If you have fusedLocation.getLastLocation() in onCreate() method,move that code to onResume() method.because when you open the App from recent apps tray ,onStart() method will invoked first then onResume() not onCreate().Now enable location ,open the app from recent app tray,check location is updated or not.
@Override
protected void onResume() {
super.onResume();
if (isLocationServiceEnabled) {
startLocationUpdates();
}
}