0

I need the location on app startup. Am following this tut with fusedlocationapi to get LastLocation: https://developer.android.com/training/location/retrieve-current.html

It's working perfectly when the AsyncTask for looking up address is executed via a button, but when I put the same snippet in oncreate or onstart, then am getting NPE, probably as it's taking long for a location fix.

I want the activity to load, show the ProgressBar & then display location, without having to click the button.

A workaround would be to execute the AsyncTask after a short delay in onstart, but am guessing there should be something more efficient.

public class MainActivity extends ActionBarActivity implements
    ConnectionCallbacks, OnConnectionFailedListener{


protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    mAddress = (TextView) findViewById(R.id.address);
    mActivityIndicator =
            (ProgressBar) findViewById(R.id.address_progress);

    buildGoogleApiClient();
}

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


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

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

public void onConnected(Bundle connectionHint) {
    mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}

private class GetAddressTask extends AsyncTask<Location, Void, String>
{
    Context mContext;
    public GetAddressTask(Context context) {
        super();
        mContext = context;
    }
    @Override
    protected String doInBackground(Location... params) {
        Geocoder geocoder =
                new Geocoder(mContext, Locale.getDefault());
        // Get the current location from the input parameter list
        Location loc = params[0];
        // Create a list to contain the result address
        List<Address> addresses = null;
        try {
            addresses = geocoder.getFromLocation(loc.getLatitude(),
                    loc.getLongitude(), 1);
        } catch (IOException e1) {
            Log.e("LocationSampleActivity",
                    "IO Exception in getFromLocation()");
            e1.printStackTrace();
            return ("IO Exception trying to get address");
        } catch (IllegalArgumentException e2) {
            // Error message to post in the log
            String errorString = "Illegal arguments " +
                    Double.toString(loc.getLatitude()) +
                    " , " +
                    Double.toString(loc.getLongitude()) +
                    " passed to address service";
            Log.e("LocationSampleActivity", errorString);
            e2.printStackTrace();
            return errorString;
        }
        // If the reverse geocode returned an address
        if (addresses != null && addresses.size() > 0) {
            // Get the first address
            Address address = addresses.get(0);
            /*
             * Format the first line of address (if available),
             * city, and country name.
             */
            String addressText = String.format(
                    "%s, %s, %s",
                    // If there's a street address, add it
                    address.getMaxAddressLineIndex() > 0 ?
                            address.getAddressLine(0) : "",
                    // Locality is usually a city
                    address.getLocality(),
                    // The country of the address
                    address.getCountryName());
            // Return the text
            return addressText;
        } else {
            return "No address found";
        }
    }

    @Override
    protected void onPostExecute(String address) {
        // Set activity indicator visibility to "gone"
        mActivityIndicator.setVisibility(View.GONE);
        // Display the results of the lookup.
        mAddress.setText(address);
    }
}

public void getAddress(View v) {
    // Ensure that a Geocoder services is available
    if (Build.VERSION.SDK_INT >=
            Build.VERSION_CODES.GINGERBREAD
            &&
            Geocoder.isPresent()) {
        // Show the activity indicator
        mActivityIndicator.setVisibility(View.VISIBLE);
        /*
         * Reverse geocoding is long-running and synchronous.
         * Run it on a background thread.
         * Pass the current location to the background task.
         * When the task finishes,
         * onPostExecute() displays the address.
         */
        (new GetAddressTask(this)).execute(mLastLocation);
    }
}
Ishaan Garg
  • 3,014
  • 3
  • 25
  • 28
  • first: please check null in your doInBackground. your call to get last location doesn't guarantee it always return value. and that is not a good practice though. consider to create a LocationRequest inside onConmected(), and request a location callback/listener and get your location/run your getAddress method inside the onLocationChanged(Location loc). about the implementation, it's on the air, please Google it – Nguyễn Hoài Nam Dec 27 '14 at 00:32

3 Answers3

1

API calls will fail unless GoogleApiClient is in the connected state. You request it to connect in onStart and it's asynchronously connected. You obviously shouldn't put a random delay in onStart and you shouldn't do so in an AsyncTask as that will be flaky.

Make your API call in (or after) the onConnected callback happens. Perhaps with conditional logic so that if your app is resumed the call isn't made again.

PaulR
  • 3,223
  • 1
  • 21
  • 32
0

Since this is a direct answer, I will post here.

In short: please have a look at my gist

https://gist.github.com/2022a4d2c21d73f042eb

Explaination:

After GoogleApiClient successfully connected to Google service, it cannot get the full information at a blink, you should be aware of that. So basically, You will want to make a request to what API you want to use (FuseLocation API, Google Fit API ...). And onConnected() method is where you start the request. Each API that GoogleAPIClient gives you will come with its own way of implement the callback, you should have a look at the documentation too. So please consider to see my Gist and give feedback if it works.

Nguyễn Hoài Nam
  • 1,130
  • 1
  • 9
  • 20
0

Do the following (btw, I tried to enter the code in the correct format, but it won't take so sorry if the below looks scruffy):

  1. In onCreate, set up your GoogleApiClient:

    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
    
    mLocationRequest = LocationRequest.create()
            .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)
            .setInterval(10000).setFastestInterval(5000);
    
  2. In onStart / onResume, connect to your GoogleApiClient:

    mGoogleApiClient.connect();

  3. In onPause, disconnect from GoogleApiClient:

    if (mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        mGoogleApiClient.disconnect();
    }
    
  4. In onConnect callback method,do the following:

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

  5. In onLocationChanged, get your location and then disconnect:

    public void onLocationChanged(Location location) {

    mLastLocation = location;
    if (mGoogleApiClient.isConnected() && mLastLocation != null) {
        Log.e("LocationChanged", String.valueOf(location.getLatitude()) + " Longitude: " + String.valueOf(location.getLongitude()));
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
        mGoogleApiClient.disconnect();
    }
    

    }

Simon
  • 19,658
  • 27
  • 149
  • 217