Ch 14 User Location and Permission

Hello,

I have an error in the function getCurrentLocation.

private fun getCurrentLocation() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestLocationPermissions()
} else {
fusedLocationClient.lastLocation.addOnCompleteListener {
if (it.result != null) {
val latLng = LatLng(it.result.latitude, it.result.longitude)
map.addMarker(MarkerOptions().position(latLng).title(“You are here!”))
val update = CameraUpdateFactory.newLatLngZoom(latLng, 16.0f)
map.moveCamera(update)
} else {
Log.e(TAG, “No location found”)
}
}
}
}

I have the following error with the instructions it.result.latitude and it.result.longitude:

smart cast to 'Location' is impossible, because it.result is a property that has open or custom getter.

I found the following things:

When I use implementation ‘com.google.android.gms:play-services-location:15.0.1’ it works, but when I use the last version implementation ‘com.google.android.gms:play-services-location:16.0.0’ it failed.

I fixed with the following code, but I want to understand its behavior a little bit.

private fun getCurrentLocation() {
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
requestLocationPermissions()
} else {
fusedLocationClient.lastLocation.addOnCompleteListener {
if (it.result != null) {
val result = it.result
if (result?.latitude != null && result?.longitude != null) {
val latLng = LatLng(result?.latitude, result?.longitude)
map.addMarker(MarkerOptions().position(latLng).title(“You are here!”))
val update = CameraUpdateFactory.newLatLngZoom(latLng, 16.0f)
map.moveCamera(update)
}
} else {
Log.e(TAG, “No location found”)
}
}
}
}

Thank you very much.

The compiler error occurs in the first block of code because of what could happen between these two lines:

if (it.result != null) {
// it.result was not null, but now we switch to a different thread, which executes some other code,
// and among other things it makes it.result null, and when this thread gets active again,
// this next line would fail because it.result is null
val latLng = LatLng( it.result.latitude, it.result.longitude)

Not very likely, put possible in principle.

When you do this in your fixed code:
if (it.result != null) {
val result = it.result
result is now a local variable and immutable, and can’t be affected by other threads. It could be null, though, because it.result might be null when assigned, as above in the first block (again very unlikely). So you still have to use the result? syntax.

If you change it a bit, you can make result local and known to be not null:
val result = it.result
if (result != null {
// the compiler knows result is local and immutable (its a val),
// and In this block it knows it is not null, so you should not need the ? marks
if (result.latitude != null …
}

1 Like

@tblank Do you have any feedback about this? Thank you - much appreciated! :]

Sorry for the late reply on this. @sgerrard gave a good explanation of the general case where a mutable variable can be changed in a multi-threaded environment and cause some heartache. His solution is a good fix for the problem.

I would only add that for this particular situation, the compiler’s complaint is Smart cast to 'Location' is impossible, because 'it.result' is a property that has open or custom getter. Since result is a custom getter, the result can change between calls to the result getter method. This is why the compiler can’t do a smart cast. By only calling result once and assigning it to a local val as suggested by @sgerrard, you will satisfy the compiler’s complaint.

Thank you for reading Android Apprentice!

1 Like

Thank you very much. – Gracias --.