LocationClient getLastLocation() return null

2019-01-02 15:46发布

问题:

Like the questions someone encountered before I tested one the nexus s(4.0.4 with google play service available) and avd (4.2.2 with google api), in both case locationclient's getLastLocation() always return null.

public class MainActivity extends Activity implements LocationListener,
        GooglePlayServicesClient.ConnectionCallbacks,
        GooglePlayServicesClient.OnConnectionFailedListener {

    private LocationClient mLocationClient;
    private LocationRequest mLocationRequest;
    boolean mUpdatesRequested = false;
    boolean mConnected = false;
    SharedPreferences mPrefs;
    SharedPreferences.Editor mEditor;
    private TextView mText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mText = (TextView) findViewById(R.id.text);
        mLocationRequest = LocationRequest.create();
        mLocationRequest
                .setInterval(LocationUtils.UPDATE_INTERVAL_IN_MILLISECONDS);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        mLocationRequest
.setFastestInterval(LocationUtils.FAST_INTERVAL_CEILING_IN_MILLISECONDS);
        mUpdatesRequested = false;
        mPrefs = getSharedPreferences(LocationUtils.SHARED_PREFERENCES,
                Context.MODE_PRIVATE);
        mEditor = mPrefs.edit();
        mLocationClient = new LocationClient(this, this, this);
    }
    @Override
    public void onStart() {
        super.onStart();
        /*
         * Connect the client. Don't re-start any requests here; instead, wait
         * for onResume()
         */
        mLocationClient.connect();
    }

    @Override
    protected void onResume() {
        super.onResume();
        // If the app already has a setting for getting location updates, get it
        if (mPrefs.contains(LocationUtils.KEY_UPDATES_REQUESTED)) {
            mUpdatesRequested = mPrefs.getBoolean(
                    LocationUtils.KEY_UPDATES_REQUESTED, false);
            // Otherwise, turn off location updates until requested
        } else {
            mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED, false);
            mEditor.commit();
        }
    }
    @Override
    public void onStop() {
        // If the client is connected
        if (mLocationClient.isConnected()) {
            stopPeriodicUpdates();
        }
        // After disconnect() is called, the client is considered "dead".
        mLocationClient.disconnect();
        super.onStop();
    }

    @Override
    public void onPause() {
        // Save the current setting for updates
        mEditor.putBoolean(LocationUtils.KEY_UPDATES_REQUESTED,
                mUpdatesRequested);
        mEditor.commit();
        super.onPause();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
    public void getLocation(View v) {
        // If Google Play Services is available
        if (isGooglePlayServicesAvailable()) {
            if (!mConnected)
                mText.setText("location client is not connected to service yet");
            else {
                // Get the current location
                Location currentLocation = mLocationClient.getLastLocation();
                // Display the current location in the UI
                mText.setText(LocationUtils.getLocationString(currentLocation));
            }
        }
    }

    private boolean isGooglePlayServicesAvailable() {

        // Check that Google Play services is available
        int resultCode = GooglePlayServicesUtil
                .isGooglePlayServicesAvailable(this);

        // If Google Play services is available
        if (ConnectionResult.SUCCESS == resultCode) {
            // In debug mode, log the status
            Log.d(LocationUtils.APPTAG, "google play service is available");

            // Continue
            return true;
            // Google Play services was not available for some reason
        } else {
            // Display an error dialog
            Dialog dialog = GooglePlayServicesUtil.getErrorDialog(resultCode,
                    this, 0);
            if (dialog != null) {
                Log.e(LocationUtils.APPTAG,
                        "google play service is unavailable");
            }
            return false;
        }
    }

    private void stopPeriodicUpdates() {
        mLocationClient.removeLocationUpdates(this);
        // mConnectionState.setText(R.string.location_updates_stopped);
    }

    @Override
    public void onConnectionFailed(ConnectionResult arg0) {
        mConnected = false;
        Log.d(LocationUtils.APPTAG, "connection failed");
    }

    @Override
    public void onConnected(Bundle arg0) {
        mConnected = true;
        Log.d(LocationUtils.APPTAG,
                "location client connected to the location server");
        LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        lm.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0,
                new android.location.LocationListener() {
                    @Override
                    public void onStatusChanged(String provider, int status,
                            Bundle extras) {}

                    @Override
                    public void onProviderEnabled(String provider) {}

                    @Override
                    public void onProviderDisabled(String provider) {}

                    @Override
                    public void onLocationChanged(final Location location) {
                    }
                });
        Log.d(LocationUtils.APPTAG, "done trying to get location");
    }

    @Override
    public void onDisconnected() {
        // TODO Auto-generated method stub
        mConnected = false;
        Log.d(LocationUtils.APPTAG,
                "location client disconnected from the location server");
    }

    @Override
    public void onLocationChanged(Location arg0) {}

}

most of them came from examples given by google. In the code above hava tried the method like that:

LocationRequest request = LocationRequest.create();
request.setNumUpdates(1);
mLocationClient.requestLocationUpdates(request, this);

and

LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        lm.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0,
                new android.location.LocationListener() {
                    @Override
                    public void onStatusChanged(String provider, int status,Bundle extras) {}

                    @Override
                    public void onProviderEnabled(String provider) {}

                    @Override
                    public void onProviderDisabled(String provider) {}

                    @Override
                    public void onLocationChanged(final Location location) {}
                });

In onConnected() before calling getLastLocation(), but still get no luck. Where's the mistake, Thanks in advance.

回答1:

Currently the Fused Location Provider will only maintain background location if at least one client is connected to it. Once the first client connects, it will immediately try to get a location. If your activity is the first client to connect and you call getLastLocation() right away in onConnected(), that might not be enough time for the first location to come in.



回答2:

I had the same problem when following the instructions from the tutorial. On phone it worked and on the (Genymotion) emulator it didn't.

Solution

In your AndroidManifest.xml, change this:

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />

to this:

<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />

...and you get the location immediately. No need to change your code (to listen to location updates).



回答3:

The issue can also be caused by your device does not have "Wi-Fi & mobile network location" enabled.

LocationClient (the fused location provider) uses both GPS and WiFi. GPS takes a while to find your location while wifi is much faster. However, if any one of these 2 services is connected, the call back method onConnected will be called. And if you are trying to call LocationClient.getLastLocation() in the onConnected method immediately, it is mostly likely that you will get null value if your wifi location service is disabled. It is just because GPS is simply not fast enough.

To solve the problem for yourself locally, enable "Wi-Fi & mobile network location". You can do it by going to "Settings > Personal > Location access > Wi-Fi & mobile network location".

However, if you want to solve the issue for the users of your app, you'd better check whether getLastLocation() returns null. If it does, prompt your user to enable the service just like google map does.

Hopefully, that helps.



回答4:

I was facing a similar issue.

Call mLocationClient.getLastLocation() in onConnected or after the Connection to Google Play Services has been established. If you're calling this method before the Location Client is connected, the location returned would be null.

You could check if the location client is connected by mLocationClient.isConnected().

Hope this helps.



回答5:

You must check if user has enabled location via Wi-Fi/GSM or GPS. If there isn't any available location provider, you get null.

This code displays the screen with location settings:

startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));


回答6:

This is the exactly working solution, probaly in slight different circumstances. But I wanted to add some little explanation steps so that anybody gets the exact concepts:

1) onCreate() of Android Component (Eg, Activity, Fragment or Service. Note: Not IntentService), build and then connect the GoogleApiClient as below.

buildGoogleApiClient();
mGoogleApiClient.connect();

where, buildGoogleApiClient() implementation is,

protected synchronized void buildGoogleApiClient() {
        Log.i(TAG, "Building GoogleApiClient");

        mGoogleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

    }

Later on onDestroy(), you can disconnect GoogleApiClient as,

@Override
    public void onDestroy() {
        Log.i(TAG, "Service destroyed!");
        mGoogleApiClient.disconnect();
        super.onDestroy();
    }

The step 1 makes sure you build and connect the GoogleApiClient.

1) GoogleApiClient instance first time gets connected on method onConnected(). Now, your next step should look onConnected() method.

@Override
    public void onConnected(@Nullable Bundle bundle) {
        Log.i(TAG, "GoogleApiClient connected!");
        buildLocationSettingsRequest();
        createLocationRequest();
        location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
        Log.i(TAG, " Location: " + location); //may return **null** because, I can't guarantee location has been changed immmediately 
    }

Above, you called a method createLocationRequest() to create location request. The method createLocationRequest() looks like below.

protected void createLocationRequest() {
        //remove location updates so that it resets
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this); //Import should not be **android.Location.LocationListener**
    //import should be **import com.google.android.gms.location.LocationListener**;

        mLocationRequest = new LocationRequest();
        mLocationRequest.setInterval(10000);
        mLocationRequest.setFastestInterval(5000);
        mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        //restart location updates with the new interval
        LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);

    }

3) Now, on onLocationChange() callback of LocationListener interface, you get new location.

@Override
    public void onLocationChanged(Location location) {
        Log.i(TAG, "Location Changed!");
        Log.i(TAG, " Location: " + location); //I guarantee,I get the changed location here

    }

You get the result like this in Logcat: 03-22 18:34:17.336 817-817/com.LiveEarthquakesAlerts I/LocationTracker: Location: Location[fused 37.421998,-122.084000 acc=20 et=+15m35s840ms alt=0.0]

To be able to do these three steps, you should have configured your build.gradle as below:

 compile 'com.google.android.gms:play-services-location:10.2.1'


回答7:

I faced similar issues on my tests with Samsung phones (highly customized android, and no developer support).

LocationManager and LocationClient dont get the GPS from the providers. They need to be kickstarted everytime you need the location from them. Do this before your LocationManager.getLastKnownLocation OR LocationClient.getLastLocation calls. These API's will return.

YOUR_APPLICATION_CONTEXT.getLocationManager().requestLocationUpdates(
    LocationManager.NETWORK_PROVIDER, 0, 0, new LocationListener() {
        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
        }
        @Override
        public void onProviderEnabled(String provider) {
        }
        @Override
        public void onProviderDisabled(String provider) {
        }
        @Override
        public void onLocationChanged(final Location location) {
        }
    });


回答8:

On SDK versions 23

You will also need to explicitly request the location permission during runtime, as per https://developer.android.com/training/permissions/requesting.html as well as having it in the manifest file.

No explicit error will occur if you do not have the permissions during runtime, the location provider will just return null.

It would help if Google documented this, and also throw an exception rather than just returning null. Returning null is about the least helpful thing to do in this situation.



回答9:

I was also facing the same issue on my app, and the only missing thing was that the app only requested for ACCESS_COARSE_LOCATION and not ACCESS_FINE_LOCATION. I added the later permission and everything worked OK.



回答10:

Google play service geolocation can't work without internet connection, indifferently for GPS. So, please check app with mobile data switched on.



回答11:

You just need an update request for the location. If with 26 Android SDK permitions is all ok:

private void setLocation(Context context) {
    GoogleApiClient googleApiClient = new GoogleApiClient.Builder(context)
            .addApi(LocationServices.API).build();
    googleApiClient.connect();

     locationRequest = LocationRequest.create();
    locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    locationRequest.setInterval(2000);
    locationRequest.setFastestInterval(2000);

    LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
    builder.setAlwaysShow(true);
    PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());
    result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
        @Override
        public void onResult(LocationSettingsResult result) {
            final Status status = result.getStatus();
            switch (status.getStatusCode()) {
                case LocationSettingsStatusCodes.SUCCESS:
                    showMessage(" All location settings are satisfied.");
                    mGoogleApiClient = new GoogleApiClient.Builder(MainActivity.this)
                            .addApi(LocationServices.API)
                            .addConnectionCallbacks(connectionCallbacks)
                            .addOnConnectionFailedListener(connectionFailedListener)
                            .build();
                            mGoogleApiClient.connect();
                    break;
                case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                    l.a(" Location settings are not satisfied. Show the user a dialog to upgrade location settings ");

                    try {
                        // Show the dialog by calling startResolutionForResult(), and check the result
                        // in onActivityResult().
                        status.startResolutionForResult(MainActivity.this, REQUEST_CHECK_SETTINGS);
                    } catch (IntentSender.SendIntentException e) {
                        showMessage("PendingIntent unable to execute request.");
                    }
                    break;
                case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                    showMessage("Location settings are inadequate, and cannot be fixed here. Dialog not created.");
                    break;
            }
        }
    });
}

and in onConnected callback method:

 @Override
    public void onConnected(@Nullable Bundle bundle) {
        l.a(3232);
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) !=
                PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                   return;
        }

            mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
                    mGoogleApiClient);
       if(null==mLastLocation){//  !!!!!!!!!!!! here it can happen !!!!!!!

                    LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, new LocationListener() {
                        @Override
                        public void onLocationChanged(Location location) {
                            mLastLocation = location;
                            locationWasFound = true;
                            sevumPora.setLocation(mLastLocation);
                            mGoogleApiClient.disconnect();
                        }
                    });
                return;
            }
        locationWasFound = true;
        sevumPora.setLocation(mLastLocation);
        mGoogleApiClient.disconnect();
    }


回答12:

I run and its working perfect in Nexus 7 device. You guys by mistake written old version LocationListener which is not used with new API.

You have to set with new LocationListener.

You need to import this class and then try.

import com.google.android.gms.location.LocationListener;

And it override the only one method as per new API

@Override
public void onLocationChanged(final Location newLocation) 
{}

Please try this way and let me know if you still facing any issue.

Thanks.



回答13:

The easiest fix, albeit slows it down a little, is to use a helper function. My problem was that it would connect, but before there was a location found, I would try to access it and hit a null pointer.

public Location getLocation(LocationClient locationClient){

    if(locationClient.getLastLocation() == null){
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return getLocation(locationClient);
    }else{
        return locationClient.getLastLocation();
    }
}

Just use this in onConnected and set whatever you wanted the location to be using this function, passing your location client.

@Override
public void onConnected(Bundle dataBundle) {

    Location temp = getLocation(mLocationClient);
    mLocation = temp;
}

Also if you don't want to get the location from onConnected for whatever reason, you can use the same helper function anywhere as long as you pass your locationClient.



标签: