简体   繁体   中英

Getting NullPointerException when call Location from Activity

This is my activity and I'd like to get into it the location. I have decoupled the Activity and the geolocation function

package com.salvo.weather.android.activity;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;


import com.google.android.gms.common.api.GoogleApiClient;
import com.salvo.weather.android.R;
import com.salvo.weather.android.geolocation.CurrentGeolocation;


public class TodayActivity extends Activity {

    public static final String TAG = TodayActivity.class.getSimpleName();

    protected GoogleApiClient mGoogleApiClient;

    protected CurrentGeolocation mCurrentGeolocation;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_today);

        mCurrentGeolocation = new CurrentGeolocation(this);
        mGoogleApiClient = mCurrentGeolocation.getmGoogleApiClient();
    }

    @Override
    protected void onStart() {
        super.onStart();
        mGoogleApiClient.connect();

        Log.i(TAG, String.valueOf(mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation().getLatitude()));
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_today, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }

        return super.onOptionsItemSelected(item);
    }
}

Here is the geolocation function:

package com.salvo.weather.android.geolocation;

import android.content.Context;
import android.location.Location;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;


import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationServices;
import com.salvo.weather.android.entity.CurrentGeolocationEntity;

/**
 * Created by mazzy on 30/05/15.
 */
public class CurrentGeolocation
        implements ConnectionCallbacks, OnConnectionFailedListener {

    private Context mcontext;

    private GoogleApiClient mGoogleApiClient;
    private CurrentGeolocationEntity mCurrentGeolocationEntity;

    public CurrentGeolocation(Context context) {
        mcontext = context;
        mCurrentGeolocationEntity = new CurrentGeolocationEntity();
        buildGoogleApiClient();
    }

    protected synchronized void buildGoogleApiClient() {
        this.mGoogleApiClient = new GoogleApiClient.Builder(this.mcontext)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(LocationServices.API)
                .build();
    }

    @Override
    public void onConnected(Bundle bundle) {

        Location mLastLocation = LocationServices
                .FusedLocationApi
                .getLastLocation(this.mGoogleApiClient);

        if (mLastLocation != null) {
            mCurrentGeolocationEntity.setmLastLocation(mLastLocation);
        } else {
            Toast.makeText(this.mcontext, "No location detected", Toast.LENGTH_LONG).show();
        }
    }

    @Override
    public void onConnectionSuspended(int i) {
        // The connection to Google Play services was lost for some reason. We call connect() to
        // attempt to re-establish the connection.
        // TODO: add some verbose message
    }

    @Override
    public void onConnectionFailed(ConnectionResult connectionResult) {
        // Refer to the javadoc for ConnectionResult to see what error codes might be returned in
        // onConnectionFailed.
    }

    public GoogleApiClient getmGoogleApiClient() {
        return mGoogleApiClient;
    }

    public CurrentGeolocationEntity getmCurrentGeolocationEntity() {
        return mCurrentGeolocationEntity;
    }
}

The class CurrentGeolocationEntity is a simple POJO where I store the Location representing the last location known.

Unforuntately I get the error:

Attempt to invoke virtual method 'double android.location.Location.getLatitude()' on a null object reference

I don't know how to figure out it. Do you have any advice on regard?

It looks like you have a race-condition, and you're requesting a location from your CurrentGeolocation class before the SDK has had time to connect.

You could add a isConnected() method to your CurrentGeolocation class, and set a boolean value in the onConnected() callback.

Then, always call isConnected() before you call mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation() .

Another thing to mention is that getLastLocation() has a high probability of returning null, since it does not explicitly make a location request. The location that you get from this method can also be very old, and might not reflect the current location at all.

The first fix you should make is this, do a null check on the Location object before you use it:

@Override
protected void onStart() {
    super.onStart();
    mGoogleApiClient.connect();

    //use a temp Location object:
    Location loc = mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation();
    //null check:
    if (loc != null){

       Log.i(TAG, String.valueOf(loc.getLatitude()));
    }
}

The next change you should make is to register a location listener in your CurrentGeolocation class, which will explicitly request a new location from the system.

    mLocationRequest = new LocationRequest();
    mLocationRequest.setInterval(10);
    mLocationRequest.setFastestInterval(10);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

    mLocationRequest.setSmallestDisplacement(0.1F);

    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);

Then add a onLocationChanged() callback:

@Override
public void onLocationChanged(Location mLastLocation) {

    mCurrentGeolocationEntity.setmLastLocation(mLastLocation);

}

For more info, take a look at the code in this answer .

Note that you should un-register the location listener as soon as possible to minimize battery drain of your app:

 LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);

Your problem is in onStart() in your Activity, at this line:

Log.i(TAG, String.valueOf(mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation().getLatitude()));

The problem is that the following line:

mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation()

returns null. I can only guess now, but I think that when your activity first starts you have no last location and so that method returns null. Try the following code:

@Override
protected void onStart() {
    super.onStart();
    mGoogleApiClient.connect();
    if (mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation() != null)
        Log.i(TAG, String.valueOf(mCurrentGeolocation.getmCurrentGeolocationEntity().getmLastLocation().getLatitude()));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM