Search code examples
androidlocationgoogle-play-servicesfusedlocationproviderapi

Get location address on app startup


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);
    }
}

Solution

  • 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.