In Android, there is a android.location.LocationListener
class, and the Android Operating System updates locations using the class as below:
It says that Location is @NonNull, but in reality the OS does return null sometimes, which crashes the app.
object : LocationListener {
override fun onLocationChanged(location: Location) {
doSomethingWithLocation(location) // App crashes here, because location is null
}
}
Is there some way to tell the compiler that Location
is actually Location?
?
CodePudding user response:
I don't believe there is way to discard the nullability information coming from java. in your case this is a problem because as pointed out by @RobCo, kotlin will automatically insert non-null assertion for location and the assertion will fail.
So it seems the only solution you are left with is to use a java wrapper type which will only provide you with non-null location values, which you can use in your kotlin code. this wrapper could look like
import androidx.core.util.Consumer;
public class LocationHandler{
LocationHandler(Consumer<Location> delegate){
this.delegate = delegate;
}
private Consumer<Location> delegate;
private LocationListener _listener = new LocationListener() {
@Override
public void onLocationChanged(@NonNull Location location) {
// Only forward non-null location values
if(location != null){ delegate.accept(location); }
else {
Log.d("LocationHandler", "Received null Location");
}
}
};
public LocationListener getListener(){
return _listener;
}
}
Now in your kotlin code you can use it as
class MainActivity : AppCompatActivity() {
private val locationDelegate = Consumer<Location> {
// Do something with non-null location
}
override fun somFun {
val locationHandler = LocationHandler(locationDelegate)
val locMan = getSystemService(Context.LOCATION_SERVICE) as LocationManager
locMan.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
0L,
0F,
locationHandler.listener)
}
}
CodePudding user response:
I found two workarounds for this.
First, if the interface contains only a single abstract method, we can use SAM conversion and override the type of the parameter to Location?
like this:
val listener = LocationListener { location: Location? ->
doSomethingWithLocation(location)
}
What is interesting, we can override the parameter if using SAM conversion, but we can't if using full syntax of subtyping. It seems LocationListener
is SAM, so this solution could work in your case.
If the first solution is not possible, we can override the interface in Java:
public interface MyLocationListener extends LocationListener {
@Override
void onLocationChanged(@Nullable Location location);
}
val listener = object : MyLocationListener {
override fun onLocationChanged(location: Location?) {
doSomethingWithLocation(location)
}
}