0

Would you please explain me why getLongitude and getLatitude returns a NullPointerException?

The truth is the problem already has been solved, but there's one thing that is bugging me for this might be asked by our thesis coordinator. The situation is that I have to get the user's current coordinates (latitude and longitude). I've decided to use FusedLocationClient.getLastLocation() in order to get the coordinates.

mFusedLocationClient = getFusedLocationProviderClient(this);

//app already crashes once the line below is executed

mFusedLocationClient.getLastLocation().addOnSuccessListener(this, new OnSuccessListener<Location>() {
            @Override
            public void onSuccess(Location location) { 
                if(location != null){
                    //myLocation is an instance variable of this class
                    myLocation = location;
                }else{
                    Toast.makeText(PlaceCategoriesActivity.this, "Location fetch failed!", Toast.LENGTH_SHORT).show();
                }
            }
        });

        latitude = myLocation.getLatitude(); //this line throws NullPointerException 
        longitude = myLocation.getLongitude();

The app seems to compile fine but then it crashes, upon inspecting by adding a breakpoint for debugging on myLocation.getLatiude() and myLocation.getLongititude(), it turns out that this method invokes a null pointer exception, which for me is weird because myLocation already has referenced the location object brought by the onSuccess method of the addOnSuccessListener.

I've already solved the problem by moving the last two lines of code and putting in within the listener like this:

        mFusedLocationClient = getFusedLocationProviderClient(this);

        mFusedLocationClient.getLastLocation().addOnSuccessListener(this, new OnSuccessListener<Location>() {
            @Override
            public void onSuccess(Location location) {
                if(location != null){
                    myLocation = location;

                    //moved the 2 lines of code and it now works
                    latitude = myLocation.getLatitude();
                    longitude = myLocation.getLongitude();
                }else{
                    Toast.makeText(PlaceCategoriesActivity.this, "Location fetch failed!", Toast.LENGTH_SHORT).show();
                }
            }
        });

        //Toast returns a value of 0 on latitude and 0 on longitude
        Toast.makeText(this, "Lat: " + latitude + "Long: + " + longitude, Toast.LENGTH_SHORT).show();

The funny thing is that the longitude and latitude is shown on the Toast and both has a value of 0.

Mark Rotteveel
  • 82,132
  • 136
  • 114
  • 158

4 Answers4

3

which for me is weird because myLocation already has referenced the location object brought by the onSuccess method of the addOnSuccessListener

Let's simplify your first code listing:

mFusedLocationClient = getFusedLocationProviderClient(this);

        latitude = myLocation.getLatitude(); //this line throws NullPointerException 
        longitude = myLocation.getLongitude();

Here, myLocation has not been assigned a value. If you have not assigned a value to myLocation in preceding lines, myLocation will be null, and you will get a NullPointerException.

Now, let's restore some lines to that code snippet:

mFusedLocationClient = getFusedLocationProviderClient(this);

//app already crashes once the line below is executed

mFusedLocationClient.getLastLocation().addOnSuccessListener(this, new OnSuccessListener<Location>() {
            @Override
            public void onSuccess(Location location) { 
            }
        });

        latitude = myLocation.getLatitude(); //this line throws NullPointerException 
        longitude = myLocation.getLongitude();

The last word in addOnSuccessListener() is "listener". This suggests that you are registering an event listener. Usually — though not always — such a listener is only invoked on future events. It is likely that onSuccess() will not be called until sometime later. However, your program keeps running, and you then try calling getLatitude() on myLocation. Since we still have not done anything to assign a value to myLocation, it will be null, and you will crash with a NullPointerException.

And so while your first code snippet does assign a value to myLocation, you as a programmer have to assume that it will not do so until some time in the future (e.g., when we get a new location fix). Do not attempt to use myLocation until onSuccess() has assigned it a value.

CommonsWare
  • 910,778
  • 176
  • 2,215
  • 2,253
1

you should not add these lines after addOnSuccessListener:

 latitude = myLocation.getLatitude(); //this line throws NullPointerException 
 longitude = myLocation.getLongitude();

because you have to wait until mFusedLocationClient successfully gets LastLocation

i.e. move these lines into onSuccess method!


As for the reason of the nullPointerException it's because myLocation has not yet been initialized !

ATEF
  • 3,708
  • 3
  • 26
  • 50
1

You simply need to display the location when you get it, not before. That's said, just put the display code in the listener.

mFusedLocationClient = getFusedLocationProviderClient(this);

// Register a listener, that will be called when the location will be available
mFusedLocationClient.getLastLocation().addOnSuccessListener(this, new OnSuccessListener<Location>() {
    @Override
    public void onSuccess(Location location) {
        if (location != null) {
            // We have a location!
            myLocation = location;
            // So print it now
            latitude = myLocation.getLatitude();
            longitude = myLocation.getLongitude();
            Toast.makeText(PlaceCategoriesActivity.this, "Lat: " + latitude + "Long: + " + longitude, Toast.LENGTH_SHORT).show();
        } else {
            Toast.makeText(PlaceCategoriesActivity.this, "Location fetch failed!", Toast.LENGTH_SHORT).show();
        }
    }
});

// Loading, as we wait for listener to be called
Toast.makeText(this, "Loading...", Toast.LENGTH_SHORT).show();
Jonathan
  • 754
  • 4
  • 11
1

Example:

class LocationApi : Service(), GoogleApiClient.ConnectionCallbacks,
        GoogleApiClient.OnConnectionFailedListener, LocationListener {

    private var myGoogleApiClient: GoogleApiClient? = null
    private val myLocationRequest = LocationRequest.create()

    override fun onBind(intent: Intent?): IBinder? {
        return null
    }

    override fun onCreate() {
        super.onCreate()
        myGoogleApiClient = GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addOnConnectionFailedListener(this)
                .addConnectionCallbacks(this)
                .build()
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        myGoogleApiClient?.connect()
        return super.onStartCommand(intent, flags, startId)
    }

    override fun onDestroy() {
        myGoogleApiClient?.disconnect()

        if (myGoogleApiClient != null && myGoogleApiClient?.isConnected!!) {
            LocationServices.getFusedLocationProviderClient(this).removeLocationUpdates(locationCallBack)
        }

        super.onDestroy()
    }

    override fun onConnected(bundle: Bundle?) {
        //here u can define your interval and priority

        // request locations, check permissions ok
        if (ContextCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

            LocationServices.getFusedLocationProviderClient(this).requestLocationUpdates(myLocationRequest, locationCallBack, Looper.myLooper())
        }
    }

    override fun onConnectionSuspended(p0: Int) {
    }

    override fun onConnectionFailed(p0: ConnectionResult) {
    }

    override fun onLocationChanged(location: Location?) {
        //saving location on DB
        LocationInteractor(location)
    }

    private val locationCallBack = object : LocationCallback() {
        override fun onLocationResult(location: LocationResult) {
            super.onLocationResult(location)
            onLocationChanged(location.lastLocation)
        }
    }
}